0


高效单元测试——EasyMock技术与应用

1.EasyMock 简介

Mock 对象的弊端?

手动的构造 Mock 对象会给开发人员带来额外的编码量

为创建 Mock 对象而编写的代码很有可能引入错误

根据现有的接口或类动态生成 Mock 对象,能避免额外的编码工作,同时也降低了引入错误的可能。

EasyMock 是一套用于通过简单的方法对于给定的接口生成 Mock 对象的类库。

提供对接口的模拟,能够通过录制、回放、检查三步来完成大体的测试过程,可以验证方法的调用种类、次数、顺序,可以令 Mock 对象返回指定的值或抛出指定异常。

通过 EasyMock,可以方便的构造 Mock 对象从而使单元测试顺利进行

easymock的官网 https://easymock.org/

2.EasyMock 实例

public class User {
    private String id;
    private String name;
    private int age;
    public String getId() {return id;}
    public void setId(String id) {this.id = id;}
    public String getName() {return name;}
    public void setName(String name) {this.name = name;}
    public int getAge() {return age;}
    public void setAge(int age) {this.age = age;}
    public User() {super();}
    public User(String id, String name, int age) {
        super();
        this.id = id;
        this.name = name;
        this.age = age;
    }
}
public interface UserService {
    User query(String userId);
}
public interface UserDao {
    User getById(String userId);
}
public class UserServiceImpl implements UserService {
    private UserDao userDao;
    public User query(String userId) {
        return userDao.getById(userId);
    }
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
}

定义两个interface:

  UserService 和 UserDao, 并给出了实现类UserServiceImpl 。 UserServiceImpl依赖到UserDao,通过setter方法可以注入一个UserDao实现。

使用mock object 技术测试:UserServiceImpl

手动Mock的测试方法

class MockUserDao implements UserDao {
    private Map<String, User> users = new HashMap<String, User>();
    public void addUser(String userid, User user) {
        users.put(userid, user);
    }
    public User getById(String userId) {
        // TODO Auto-generated method stub
        return users.get(userId);
    }

完整测试方法

public class UserServiceImplTestWithMockObject {
    @Test
    public void testQuery() {
        User expectedUser = new User();
        expectedUser.setId("1001");
        expectedUser.setAge(30);
        expectedUser.setName("user-1001");
        
        MockUserDao mud = new MockUserDao();
        mud.addUser("1001", expectedUser);
 
        UserServiceImpl service = new UserServiceImpl();
        service.setUserDao(mud);
        User user = service.query("1001");
        assertNotNull(user);
        assertEquals("1001", user.getId());
        assertEquals(30, user.getAge());
        assertEquals("user-1001", user.getName());
    }
}

如果使用EasyMock技术,如何实现,带来多大的方便???

3.EasyMock 模型

3.1、record-replay-verify 模型

record-replay-verify 模型容许记录mock对象上的操作然后重演并验证这些操作。

是目前mock框架领域最常见的模型

几乎所有的mock框架都是用这个模型

3.2、record

UserDao userDao = EasyMock.createMock(UserDao.class);
EasyMock.expect(userDao.getById("1001")).andReturn(expectedUser);

创建mock对象,并期望这个mock对象的方法被调用,同时给出希望这个方法返回的结果。

这就是"记录mock对象上的操作", 同时也会看到"expect"这个关键字。

在record阶段,需要给出的是对mock对象的一系列期望:若干个mock对象被调用,依从给定的参数,顺序,次数等,并返回预设好的结果(返回值或者异常).

** 3.3、replay**

EasyMock.replay(mockUserDao);
        UserServiceImpl service = new UserServiceImpl();
        service.setUserDao(mockUserDao);
        User user = service.query("1001");    

在replay阶段,主要测试对象被创建,前面在record阶段创建的相关依赖被关联到主要测试对象,然后执行被测试的方法,以模拟真实运行环境下主要测试对象的行为。

在测试方法执行过程中,主要测试对象的内部代码被执行,同时和相关的依赖进行交互:

以一定的参数调用依赖的方法,获取并处理返回。

期待这个过程如在record阶段设想的交互场景一致,即期望在replay阶段所有在record阶段记录的行为都将被完整而准确的重新演绎一遍,从而到达验证主要测试对象行为的目的。

** 3.4、verify**

nverify阶段,将验证测试的结果和****交互行为

assertNotNull(user);
assertEquals("1001", user.getId());
assertEquals(30, user.getAge());
assertEquals("user-1001", user.getName());

EasyMock.verify(userDao);

验证结果,即主要测试对象的测试方法返回的结果(对于异常测试场景则是抛出的异常)是否如预期,通常这个验证过程需要自行编码实现

                    EasyMock.verify(userDao);

                   验证交互行为,典型如依赖是否被调用,调用的参数,顺序和次数,这部分的验证过程通常是由mock框架来自动完成。

3.5、easymock部分功能说明

*1. 创建mock*对象

UserDao mockUserDao = Easymock.createMock(UserDao.class);

*2. 记录mock*对象期望的行为

Easymock.expect(mockUserDao.getById("1001")).andReturn(expectedUser);

这里记录了mock对象的行为:getById()方法被调用,调用次数为1

(easymock之中如果没有明确指出调用次数,默认为1),参数为"1001",expectedUser将作为返回值

3. 进入replay阶段

** Easymock.replay(mockU****serDao); **

4. 对mock对象执行验证

Easymock.verify(mockUserDao);

5. 指定期望的调用次数

Easymock.expect(mockUserDao.getById("1001")).andReturn(expectedUser).times(3);

6. 指定抛出期望的异常

Easymock.expect(*mockU*serDao.getById("1001")).andThrow(new RuntimeException("no user exist"));

7. 记录void 方法的行为:

如果mock对象的方法是void,则需要使用expectLastCall():

mockUserDao.someVoidMethod();

Easymock.expectLastCall();

和Easymock.expect()一样,同样支持指定调用次数,抛出异常等:*

Easymock.expectLastCall().times(3);

Easymock.expectLastCall().andThrow(new RuntimeException("some error"));

8. 灵活的参数匹配

Easymock.expect(*mockU*serDao.getById(Easymock.isA(String.class))).andReturn(expectedUser);

类似的还有anyInt(),anyObject(), isNull() , same(), startsWith()等诸多实现。

4.EasyMock 应用

4.1、Easymock对AccountService进行测试

4.2、用Easymock对WebClient的测试

标签: 单元测试

本文转载自: https://blog.csdn.net/weixin_56941647/article/details/127695581
版权归原作者 Pig Pig Cat 所有, 如有侵权,请联系我们删除。

“高效单元测试——EasyMock技术与应用”的评论:

还没有评论