参考链接
1.Mock测试的目的两个:(1)验证方法调用;(2)指定某个方法的返回值,或者是执行特定的动作
2.所谓的mock就是创建一个类的虚假的对象,在测试环境中,用来替换掉真实的对象,以达到两大目的
验证这个对象的某些方法的调用情况,调用了多少次,参数是什么等等
指定这个对象的某些方法的行为,返回特定的值,或者是执行特定的动作
3. (1)UserManager mockUserManager = Mockito.mock(UserManager.class); 需要我们创建了一个mock对象;
(2)我们必须在调用 loginPresenter.login() 之前,把 mUserManager 引用换成 mockUserManager 所引用的mock对象。最简单的办法,就是加一个setter:
public class LoginPresenter {
private UserManager mUserManager = new UserManager();
public void login(String username, String password) {
if (username == null || username.length() == 0) return;
if (password == null || password.length() < 6) return;
mUserManager.performLogin(username, password);
}
public void setUserManager(UserManager userManager) { //<==
this.mUserManager = userManager;
}
}
写出来的测试代码如下:
@Test
public void testLogin() throws Exception {
UserManager mockUserManager = Mockito.mock(UserManager.class);
LoginPresenter loginPresenter = new LoginPresenter();
loginPresenter.setUserManager(mockUserManager); //<==
loginPresenter.login("xiaochuang", "xiaochuang password");
Mockito.verify(mockUserManager).performLogin("xiaochuang", "xiaochuang password");
}
4.验证一个对象的某个method得到调用的方法:
Mockito.verify(mockUserManager).performLogin("xiaochuang", "xiaochuang password");
Mockito的静态方法 :对于调用次数的验证,除了可以验证固定的多少次,还可以验证最多,最少从来没有等等,方法分别 是: atMost(count), atLeast(count), never() 等等。
5. 指定mock对象的某些方法的行为: 指定某个方法的返回值,或者是执行特定的动作
(1)
Mockito.verify(mockUserManager).performLogin(Mockito.anyString(), Mockito.anyString());
LoginPresenter 的 login 方法是如下:
public void login(String username, String password) {
if (username == null || username.length() == 0) return;
//假设我们对密码强度有一定要求,使用一个专门的validator来验证密码的有效性
if (mPasswordValidator.verifyPassword(password)) return; //<==
mUserManager.performLogin(null, password);
}
这种指定mock对象的某个方法,让它返回特定值的写法如下:
Mockito.when(mockObject.targetMethod(args)).thenReturn(desiredReturnValue);
//先创建一个mock对象
PasswordValidator mockValidator = Mockito.mock(PasswordValidator.class);
//当调用mockValidator的verifyPassword方法,同时传入"xiaochuang_is_handsome"时,返回true
Mockito.when(mockValidator.verifyPassword("xiaochuang_is_handsome")).thenReturn(true);
//当调用mockValidator的verifyPassword方法,同时传入"xiaochuang_is_not_handsome"时,返回false
Mockito.when(validator.verifyPassword("xiaochuang_is_not_handsome")).thenReturn(false);
//当调用mockValidator的verifyPassword方法时,返回true,无论参数是什么
Mockito.when(validator.verifyPassword(anyString())).thenReturn(true);
(2)
指定一个方法执行特定的动作,这个功能一般是用在目标的方法是void类型的时候:
LoginPresenter 的 login() 方法:
public void loginCallbackVersion(String username, String password) {
if (username == null || username.length() == 0) return;
//假设我们对密码强度有一定要求,使用一个专门的validator来验证密码的有效性
if (mPasswordValidator.verifyPassword(password)) return;
//login的结果将通过callback传递回来。
mUserManager.performLogin(username, password, new NetworkCallback() { //<==
@Override
public void onSuccess(Object data) {
//update view with data
}
@Override
public void onFailure(int code, String msg) {
//show error msg
}
});
}
我们想让 mUserManager 直接调用传入的 NetworkCallback 的 onSuccess 或 onFailure 方法。这种指定mock对象执行特定的动作的写法如下:
Mockito.doAnswer(desiredAnswer).when(mockObject).targetMethod(args); 传给 doAnswer() 的是一个 Answer 对象,我们想要执行什么样的动作,就在这里面实现
Mockito.doAnswer(new Answer() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
//这里可以获得传给performLogin的参数
Object[] arguments = invocation.getArguments();
//callback是第三个参数
NetworkCallback callback = (NetworkCallback) arguments[2];
callback.onFailure(500, "Server error");
return 500;
}
}).when(mockUserManager).performLogin(anyString(), anyString(), any(NetworkCallback.class));
当调用 mockUserManager 的 performLogin 方法时,会执行answer里面的代码,我们上面的例子是直接调用传入的 callback 的 onFailure 方法,同时传给 onFailure 方法500和"Server error"。
自己的实例:采用第5点方法(1):
// 根据客户编号修改客户状态
@Test
public void updateCustomerStatusByCustomerNo(){
String traceLogId = "110";
String customerNo = "100001";
CustomerBaseInfoReqDTO reqDTO = new CustomerBaseInfoReqDTO();
reqDTO.setCustomerNo(customerNo);
reqDTO.setUpdatedAt(new Date());
reqDTO.setUpdatedBy("nazi");
reqDTO.setStatus(CustBaseInfoStatus.CLOSE.getCode());
Mockito.when(customerBaseInfoBiz.updateCustomerStatusByCustomerNo(Mockito.any(CustomerBaseInfoReqDTO.class))).thenReturn(Boolean.TRUE);
Result result = customerBaseInfoService.updateCustomerStatusByCustomerNo(reqDTO, traceLogId);
Assert.assertTrue(result.isSuccess());
System.out.println("----------------------分隔线--------------------------");
Mockito.when(customerBaseInfoBiz.updateCustomerStatusByCustomerNo(
Mockito.any(CustomerBaseInfoReqDTO.class))).thenThrow(BizServerException.class);
result = customerBaseInfoService.updateCustomerStatusByCustomerNo(reqDTO, traceLogId);
Assert.assertFalse(result.isSuccess());
System.out.println("----------------------分隔线--------------------------");
Mockito.when(customerBaseInfoBiz.updateCustomerStatusByCustomerNo(
Mockito.any(CustomerBaseInfoReqDTO.class))).thenThrow(NullPointerException.class);
result = customerBaseInfoService.updateCustomerStatusByCustomerNo(reqDTO, traceLogId);
Assert.assertFalse(result.isSuccess());
}
// 根据客户号查询客户账户信息
@Test
public void findCustAcctByCustomerNo(){
List resDTOList = new ArrayList<>();
Mockito.when(customerAccountManager.queryByCustomerNo(Mockito.any())).thenReturn(resDTOList);
String customerNo = "0000000000020005";
Result> result = customerInfoQueryService.findCustAcctByCustomerNo(customerNo,"110");
Assert.assertTrue(result.isSuccess());
}
目的:除非指定,否者调用这个对象的默认实现,同时又能拥有验证方法调用的功能
相异点:
spy与mock的唯一区别就是默认行为不一样:spy对象的方法默认调用真实的逻辑,mock对象的方法默认什么都不做,或直接返回默认值//假设目标类的实现是这样的
public class PasswordValidator {
public boolean verifyPassword(String password) {
return "xiaochuang_is_handsome".equals(password);
}
}
@Test
public void testSpy() {
//跟创建mock类似,只不过调用的是spy方法,而不是mock方法。spy的用法
PasswordValidator spyValidator = Mockito.spy(PasswordValidator.class);
//在默认情况下,spy对象会调用这个类的真实逻辑,并返回相应的返回值,这可以对照上面的真实逻辑
spyValidator.verifyPassword("xiaochuang_is_handsome"); //true
spyValidator.verifyPassword("xiaochuang_is_not_handsome"); //false
//spy对象的方法也可以指定特定的行为
Mockito.when(spyValidator.verifyPassword(anyString())).thenReturn(true);
//同样的,可以验证spy对象的方法调用情况
spyValidator.verifyPassword("xiaochuang_is_handsome");
Mockito.verify(spyValidator).verifyPassword("xiaochuang_is_handsome"); //pass
}