Java中有效使用EasyMock编写java单元测试
http://www.zftb.cn  发布时间:2017-09-04 09:49 来源:未知 浏览:加载中

Java单元测试对于开发人员质量保证至关重要,尤其当面对一团乱码的遗留代码时,没有高覆盖率的单元测试做保障,没人敢轻易对代码进行重构。

然而单元测试的编写也不是一件容易的事情,除非使用TDD方式,否则编写出容易测试的代码不但对开发人员的设计编码要求很高,而且代码中的各种依赖也常常为单元测试带来无穷无尽的障碍。

令人欣慰的是开源社区各种优秀的Mock框架让单元测试不再复杂,本文简单介绍EasyMock等的基本常用用法。

Mock说白了就是打桩(Stub)或则模拟,当你调用一个不好在测试中创建的对象时,Mock框架为你模拟一个和真实对象类似的替身来完成相应的行为。

EasyMock

使用如下方式在Maven中添加EasyMock的依赖:

<dependency>  
      <groupId>org.easymock</groupId>  
      <artifactId>easymock</artifactId>  
      <version>3.2</version>  
      <scope>test</scope>  
</dependency>  

EasyMock使用动态代理实现模拟对象创建,其基本步骤为以下四步:

 

以数据库应用为例的被测试代码如下:

public class UserServiceImpl{  
    private UserDao dao;  
    public User query(String id) throws Exception{  
        try{  
    return dao.getById(id);  
}catch(Exception e){  
    throw e;  
}  
return null;  
}  
}  
  
public class UserDao{  
    public User getById(String id) throws Exception{  
        try{  
    return ……;  
}catch(Exception e){  
    throw e;  
}  
return null;  
}  
}  
现在希望对UserServiceImpl进行测试,而UserDao开发组只给出接口,尚未完成功能实现。

使用Mock对UserDao进行模拟来测试UserServiceImpl。

(1).基本的测试代码如下:

public class UserServiceImplTest {  
        @Test  
        public void testQuery() {  
            User expectedUser = new User();  
            user.setId(“1001”);  
            UserDao mock  = EasyMock.createMock(UserDao.class);//创建Mock对象  
            Easymock.expect(mock.getById("1001")).andReturn(expectedUser);//录制Mock对象预期行为  
            Easymock.replay(mock);//重放Mock对象,测试时以录制的对象预期行为代替真实对象的行为  
  
            UserServiceImpl  service = new UserServiceImpl();  
            service.setUserDao(mock);  
            user user = service.query("1001");//调用测试方法  
            assertEquals(expectedUser, user); //断言测试结果   
            Easymock.verify(mock);//验证Mock对象被调用  
        }  
    }   

注意:

在EasyMock3.0之前,org.easymock.EasyMock使用JDK的动态代理实现Mock对象创建,因此只能针对接口进行Mock,org.easymock.classextension.EasyMock使用CGLIB动态代理创建Mock对象,可以针对普通类进行Mock。

在EasyMock3.0之后,org.easymock.classextension.EasyMock被废弃,使用org.easymock.EasyMock可以针对接口和普通类进行Mock对象创建。

(2).调用测试设定:

如果想测试UserServiceImpl调用了UserDao的getById方法3次,则使用如下代码即可:
Easymock.expect(mock.getById("1001")).andReturn(exceptUser).times(3);  

(3).方法异常:

如果想测试UserServiceImpl在调用UserDao的getById方法时发生异常,可以使用如下代码:
Easymock.expect(mock.getById("1001")).andThrow(new RuntimeException());  
在测试UserServiceImpl时就可以使用try-catch捕获Mock的异常。

 

(4).基本参数匹配:

上面的方法在Mock UserDao的getById方法时传入了“0001”的预期值,这种方式是精确参数匹配,如果UserServiceImpl在调用是传入的参数不是“0001”就会发生Unexpect method的Mock异常,可以使用下面的方法在Mock时进行参数匹配:

Easymock.expect(mock.getById(Easymock.isA(String.class))).andReturn(exceptedUser).times(3);  

isA()方法会使用instanceof进行参数类型匹配,类似的方法还有anyInt(),anyObject(), isNull(),same(), startsWith()......

(5).数组类型参数匹配:

如果UserServiceImpl在调用UserDao的方法时传入的参数是数组,代码如下:

public class UserServiceImpl{    
    private UserDao dao;    
    public List<String> queryNames(String[] ids) throws Exception{    
        try{    
    return dao.getNames(ids);    
}catch(Exception e){    
    throw e;    
}    
return null;    
}    
}    
    
public class UserDao{    
    public List<String> getNames(String[] ids) throws Exception{    
        try{    
    return ……;    
}catch(Exception e){    
    throw e;    
}    
return null;    
}    
}  
此时有两种办法来进行参数匹配:

a.数组必须和测试给定的一致:

Easymock.expect(mock.getNames(EasyMock.aryEq(testIds))).andReturn(exceptedNames);  
b.不考虑测试数组内容:
Easymock.expect(mock.getNames(EasyMock.isA(String[].class))).andReturn(exceptedNames);  

(6).void方法Mock:

如果要Mock的方法是无返回值类型,例子如下:

public class UserDao {  
        public void updateUserById(String id) throws Exception{  
            try{  
            update…  
        }catch(Exception e){  
            throw e;   
        }  
        }  
    }  
a.正常Mock代码如下:
mock.updateUserById(“TestId”);  
EasyMock.expectLastCall().anytimes();  
b.模拟发生异常的Mock代码如下:
mock.updateUserById(“TestId”);  
EasyMock.expectLastCall().andThrow(new RuntimeException()).anytimes();  

(7).多次调用返回不同值的Mock:

对于迭代器类型的遍历代码来说,需要在不同调用时间返回不同的结果,以JDBC结果集为例代码如下:

public List<String> getUserNames () throws Exception{  
    List<String> usernames = new ArrayList<String>();  
    ResultSet rs = pstmt.executeQuery(query);  
    try {  
        while(rs.next()){  
            usernames.add(rs.getString(2));  
        }  
    } catch (SQLException e) {  
        throw e;  
    }  
 }  
在Mock结果集的next方法时如果总返回true,则代码就会陷入死循环,如果总返回false则代码逻辑根本无法执行到循环体内。

正常的测试逻辑应该是先返回几次true执行循环体,然后在返回false退出循环,使用Mock可以方便模拟这种预期的行为,代码如下:

​EasyMock.expect(rs.next()).andReturn(true).times(2).andReturn(false).times(1);  

更多的关于EasyMock的用法,请参考EasyMock官方文档:

http://easymock.org/EasyMock3_0_Documentation.html

如果你有好的win10资讯或者win10教程,以及win10相关的问题想要获得win10系统下载的关注与报道。
欢迎加入发送邮件到657025171#qq.com(#替换为@)。期待你的好消息!