使⽤mock进⾏java单元测试
Java单元测试对于开发⼈员质量保证⾄关重要,尤其当⾯对⼀团乱码的遗留代码时,没有⾼覆盖率的单元测试做保障,没⼈敢轻易对代码进⾏重构。然⽽单元测试的编写也不是⼀件容易的事情,除⾮使⽤TDD⽅式,否则编写出容易测试的代码不但对开发⼈员的设计编码要求很⾼,⽽且代码中的各种依赖也常常为单元测试带来⽆穷⽆尽的障碍。
令⼈欣慰的是开源社区各种优秀的Mock框架让单元测试不再复杂,本⽂简单介绍EasyMock,PowerMock等的基本常⽤⽤法。
Mock说⽩了就是打桩(Stub)或则模拟,当你调⽤⼀个不好在测试中创建的对象时,Mock框架为你模拟⼀个和真实对象类似的替⾝来完成相应的⾏为。
EasyMock:
使⽤如下⽅式在Maven中添加EasyMock的依赖:
org.easymock
easymock
3.2
test
EasyMock使⽤动态代理实现模拟对象创建,其基本步骤为以下四步:
以数据库应⽤为例的被测试代码如下:
public class UserServiceImpl{
private UserDao dao;
public User query(String id) throws Exception{
try{
ById(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 = ateMock(UserDao.class);//创建Mock对象
UserServiceImpl  service = new UserServiceImpl();
service.setUserDao(mock);
user user = service.query("1001");//调⽤测试⽅法
mkdirs方法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次,则使⽤如下代码即可:
(3).⽅法异常:
如果想测试UserServiceImpl在调⽤UserDao的getById⽅法时发⽣异常,可以使⽤如下代码:
在测试UserServiceImpl时就可以使⽤try-catch捕获Mock的异常。
(4).基本参数匹配:
上⾯的⽅法在Mock UserDao的getById⽅法时传⼊了“0001”的预期值,这种⽅式是精确参数匹配,如果UserServiceImpl在调⽤是传⼊的参数不是“0001”就会发⽣Unexpect method的Mock异常,可以使⽤下⾯的⽅法在Mock时进⾏参数匹配:
isA()⽅法会使⽤instanceof进⾏参数类型匹配,类似的⽅法还有anyInt(),anyObject(), isNull(),same(), startsWith()……
(5).数组类型参数匹配:
如果UserServiceImpl在调⽤UserDao的⽅法时传⼊的参数是数组,代码如下:
public class UserServiceImpl{
private UserDao dao;
public List queryNames(String[] ids) throws Exception{
try{
Names(ids);
}catch(Exception e){
throw e;
}
return null;
}
}
(1).Miock final类的静态⽅法:
如果测试代码中使⽤到了java.lang.System类,代码如下:
public class SystemPropertyMockDemo {
public String getSystemProperty() throws IOException {
Property(“property”);
}
}
如果对Property()⽅法进⾏Mock,代码如下:
@RunWith(PowerMockRunner.class)
@PrepareForTest({SystemPropertyMockDemo.class})//声明要Mock的类
public class SystemPropertyMockDemoTest {
@Test
public void demoOfFinalSystemClassMocking() throws Exception {
Assert.assertEquals(“my property”,
new SystemPropertyMockDemo().getSystemProperty());
PowerMock.verifyAll();//验证Mock对象
}
}
⾮final类的静态⽅法代码相同,注意(上述代码只能在EasyMock3.0之后版本正常运⾏)
如果要在EasyMock3.0之前版本正常Mock final类的静态⽅法,需要使⽤PowerMockito,
通过如下⽅式在maven中添加PowerMockito相关依赖:
org.powermock
powermock-api-mockito
1.5.1
test
@RunWith(PowerMockRunner.class)
@PrepareForTest({SystemPropertyMockDemo.class})
public class SystemPropertyMockDemoTest {
@Test
public void demoOfFinalSystemClassMocking() throws Exception {
PowerMockito.Property(“property”)).thenReturn(“my property”);
Assert.assertEquals(“my property”,
new SystemPropertyMockDemo().getSystemProperty());
PowerMock.verifyAll();
}
}
注意:
对于JDK的类如果要进⾏静态或final⽅法Mock时,@PrepareForTest()注解中只能放被测试的类,⽽⾮JDK的类,如上⾯例⼦中的SystemPropertyMockDemo.class。
对于⾮JDK的类如果需要进⾏静态活final⽅法Mock时, @PrepareForTest()注解中直接放⽅法所在的类,若上⾯例⼦中的System不是JDK的类,则可以直接放System.class。
@PrepareForTest({……}) 注解既可以加在类层次上(对整个测试⽂件有效),也可以加在测试⽅法上(只对测试⽅法有效)。
(2).Mock⾮静态的final⽅法:
被测试代码如下:
public class ClassDependency {
public final boolean isAlive() {
return false;
}
}
public class ClassUnderTest{
public boolean callFinalMethod(ClassDependency refer) {
return refer.isAlive();
}
}
使⽤PowerMock的测试代码如下:
@RunWith(PowerMockRunner.class)
public class FinalMethodMockDemoTest {
@Test
@PrepareForTest(ClassDependency.class)
public void testCallFinalMethod() {
ClassDependency depencency = ateMock(ClassDependency.class); //创建Mock对象
ClassUnderTest underTest = new ClassUnderTest();
Assert.assertTrue(underTest.callFinalMethod(depencency));
PowerMock.verifyAll();
}
}
(3)部分Mock和私有⽅法Mock:
如果被测试类某个⽅法不太容易调⽤,可以考虑只对该⽅法进⾏Mock,⽽其他⽅法全部使⽤被测试对象的真实⽅法,可以考虑使⽤PowerMock的部分Mock,被测试代码如下:
public class DataService {
public boolean replaceData(final String dataId, final byte[] binaryData) {
return modifyData(dataId, binaryData);
}
public boolean deleteData(final String dataId) {
return modifyData(dataId, null);
}
private boolean modifyData(final String dataId, final byte[] binaryData) {
return true;
}

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。