指定返回值
假设上面的LoginPresenter的login()方法是这么实现的:
public void login(String username, String password) {
if (username == null || username.length() == 0) return;
mPasswordValidator = new PasswordValidator();
if (!mPasswordValidator.verifyPassword(password)) return;
mUserManager.performLogin(username, password);
}
在登陆时,需要验证密码的正确性,而此时可能需要联网验证,所以会很耗时。在测试时就可以简单处理,比如直接返回true或者false。因为测试的是login()方法,和验证密码的内部逻辑没有关系,所以可以这么做,这才是单元测试该有的粒度。
指定mock对象的某个方法,让它返回特定值的写法如下:
Mockito.when(mockObject.targetMethod(args)).thenReturn(desiredReturnValue);
所以我们可以这样写:
//先创建一个mock对象
PasswordValidator mockValidator = Mockito.mock(PasswordValidator.class);
//当调用mockValidator的verifyPassword方法,同时传入"123"时,返回true
Mockito.when(mockValidator.verifyPassword("123")).thenReturn(true);
//当调用mockValidator的verifyPassword方法,同时传入"123456"时,返回false
Mockito.when(validator.verifyPassword("123456")).thenReturn(false);
由于已经指定了返回值,所以并不会执行verifyPassword()方法的真实逻辑!
此时,测试类可以这样写:
public class LoginPresenterTest {
LoginPresenter loginPresenter;
@Test
public void testLogin() {
UserManager mockUserManager = Mockito.mock(UserManager.class);
PasswordValidator mPasswordValidator = Mockito.mock(PasswordValidator.class);
Mockito.when(mPasswordValidator.verifyPassword(Mockito.anyString())).thenReturn(true);//当thenReturn(false)时,测试失败,因为mUserManager.performLogin(username, password)代码不会执行
loginPresenter = new LoginPresenter(mockUserManager, mPasswordValidator);
loginPresenter.login("aya", "123");
Mockito.verify(mockUserManager, Mockito.times(1)).performLogin("aya", "123");
}
}
注意:mock出来的对象mPasswordValidator一定要传进去!
执行特定动作
假设我们的LoginPresenter的login()方法是这样的:
public void login(String username, String password) {
if (username == null || username.length() == 0) return;
if (!mPasswordValidator.verifyPassword(password)) return;
mUserManager.performLogin(username, password, new UserManager.NetCallback() {
@Override
public void onSuccess(Object data) {
//登陆成功,用数据更新UI
}
@Override
public void onFailure(String msg) {
//登陆失败,显示msg
}
});
}
在这里,我们想进一步测试传给mUserManager.performLogin的NetCallback里面的代码,验证view得到了更新等等。在测试环境下,我们并不想依赖mUserManager.performLogin的真实逻辑,而是让mUserManager直接调用传入的NetCallback的onSuccess或onFailure方法。这种指定mock对象执行特定的动作的写法如下: Mockito.doAnswer(desiredAnswer).when(mockObject).targetMethod(args); 传给doAnswer()的是一个Answer对象,我们想要执行什么样的动作,就在这里面实现。结合上面的例子解释:
public class LoginPresenterTest {
LoginPresenter loginPresenter;
@Test
public void testLogin() {
UserManager mockUserManager = Mockito.mock(UserManager.class);
PasswordValidator mPasswordValidator = Mockito.mock(PasswordValidator.class);
Mockito.when(mPasswordValidator.verifyPassword(Mockito.anyString())).thenReturn(true);
loginPresenter = new LoginPresenter(mockUserManager, mPasswordValidator);
Mockito.doAnswer(new Answer() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
//这里可以获得传给performLogin的参数
Object[] arguments = invocation.getArguments();
//callback是第三个参数
UserManager.NetCallback callback = (UserManager.NetCallback) arguments[2];
callback.onFailure("404 Not found");
return 404;
}
}).when(mockUserManager).performLogin(Mockito.anyString(), Mockito.anyString(), Mockito.any(UserManager.NetCallback.class));
loginPresenter.login("aya", "123456");
}
}
当调用mockUserManager的performLogin方法时,会执行answer里面的代码,我们上面的例子是直接调用传入的callback的onFailure方法,同时传给onFailure方法404和"Not found"。
除了doAnswer(),mockito还提供了doNothing(),用来指定目标方法“什么都不做”;doThrow(desiredException),指定目标方法“抛出一个异常”;doCallRealMethod(),让目标方法调用真实的逻辑。
Spy
对于一个mock对象,我们可以指定返回值和执行特定的动作,当然,也可以不指定,如果不指定的话,一个mock对象的所有非void方法都将返回默认值:int、long类型方法将返回0,boolean方法将返回false,对象方法将返回null等等;而void方法将什么都不做。
如果你想实现这样的效果:指定时执行指定的动作,不指定时调用这个对象的默认实现,同时又能拥有验证方法调用的功能。那你可以使用Mockito.spy()来创建对象。
创建一个spy对象,以及spy对象的用法介绍如下:
public class PasswordValidator {
public boolean verifyPassword(String password) {
if (password == null || password.length() < 6) {
return false;
}
return true;
}
}
public class PasswordValidatorTest {
@Test
public void testSpy(){
PasswordValidator mockPasswordValidator = Mockito.mock(PasswordValidator.class);
boolean b0 = mockPasswordValidator.verifyPassword("1234567");//不指定时mock对象返回默认值
System.out.println("测试mock默认行为: " + b0);
Mockito.when(mockPasswordValidator.verifyPassword("1234567")).thenReturn(true);//mock对象指定行为:返回true
boolean b1 = mockPasswordValidator.verifyPassword("1234567");//指定时mock对象返回指定值
System.out.println("测试mock指定行为: " + b1);
PasswordValidator spyPasswordValidator = Mockito.spy(PasswordValidator.class);
boolean b2 = spyPasswordValidator.verifyPassword("1234567");//不指定时spy对象调用真实逻辑
System.out.println("测试spy默认行为: " + b2);
Mockito.when(spyPasswordValidator.verifyPassword("1234567")).thenReturn(false);//spy对象指定行为:返回false
boolean b3 = spyPasswordValidator.verifyPassword("1234567");//指定时spy对象返回指定值
System.out.println("测试spy指定行为: " + b3);
Mockito.verify(spyPasswordValidator).verifyPassword("1234567");//测试PasswordValidator.verifyPassword("1234567")调用次数,结果失败,因为上面已经调用了两次passwordValidator.verifyPassword("1234567")
}
}
运行测试方法testSpy(),输出结果如下:
通过对比可以看出:spy与mock的唯一区别就是默认行为不一样:spy对象的方法默认调用真实的逻辑,mock对象的方法默认什么都不做,或直接返回默认值。
本文内容不用于商业目的,如涉及知识产权问题,请权利人联系51Testing小编(021-64471599-8017),我们将立即处理