Moke测试学习总结:
被测试代码:
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 class LoginPresenterTest {
@Test
public void testLogin() throws Exception {
LoginPresenter loginPresenter = new LoginPresenter();
loginPresenter.login("xiaochuang", "xiaochuang password");
//验证LoginPresenter里面的mUserManager的performLogin()方法得到了调用,同时参数分别是“xiaochuang”、“xiaochuang‘s password”
...
}
}
问题1:么验证 LoginPresenter 里面的 mUserManager 的 performLogin() 方法得到了调用,以及它的参数是正确性呢?
下面开始mock的学习
Moko概念:
Mock的概念,其实很简单,我们前面也介绍过:所谓的mock就是创建一个类的虚假的对象,在测试环境中,用来替换掉真实的对象,以达到两大目的:
- 验证这个对象的某些方法的调用情况,调用了多少次,参数是什么等等
- 指定这个对象的某些方法的行为,返回特定的值,或者是执行特定的动作
验证开始:
试误1:
public class LoginPresenterTest {
@Test
public void testLogin() throws Exception {
Mockito.mock(UserManager.class);
LoginPresenter loginPresenter = new LoginPresenter();
loginPresenter.login("xiaochuang", "xiaochuang password");
UserManager userManager = loginPresenter.getUserManager();
Mockito.verify(userManager).performLogin("xiaochuang", "xiaochuang password"); //<==
}
}
试误2:
public class LoginPresenterTest {
@Test
public void testLogin() throws Exception {
UserManager mockUserManager = Mockito.mock(UserManager.class); //<==
LoginPresenter loginPresenter = new LoginPresenter();
loginPresenter.login("xiaochuang", "xiaochuang password");
Mockito.verify(mockUserManager).performLogin("xiaochuang", "xiaochuang password"); //<==
}
}
正确:
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");
}
第一种情况:
- 验证方法调用
Mockito.verify(mockUserManager, Mockito.times(1)).performLogin("xiaochuang", "xiaochuang password");
Mockito.verify(mockUserManager).performLogin(Mockito.anyString(), Mockito.anyString());
类似 anyString ,还有 anyInt, anyLong, anyDouble 等等。 anyObject 表示任何对象, any(clazz) 表示任何属于clazz的对象。在写这篇文章的时候,我刚刚发现,还有非常有意思也非常人性化的 anyCollection,anyCollectionOf(clazz), anyList(Map, set), anyListOf(clazz) 等等。
- 指定mock对象的某些方法的行为
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对象
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);
同样的,你可以用 any 系列方法来指定"无论传入任何参数值,都返回xxx":
//当调用mockValidator的verifyPassword方法时,返回true,无论参数是什么
Mockito.when(validator.verifyPassword(anyString())).thenReturn(true);
2、我们想进一步测试传给 mUserManager.performLogin 的 NetworkCallback 里面的代码,
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));
//假设目标类的实现是这样的
public class PasswordValidator {
public boolean verifyPassword(String password) {
return "xiaochuang_is_handsome".equals(password);
}
}
另外 spy的情况
@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
}