java junit测试的模拟框架 mockito和junit的gradle 不能在依赖project的build中配置 需要放在主项目中
模拟变量
方式1,使用静态方法mock(XXXObject.class) 生成模拟数据,如
1
2
// mock creation
List mockedList = mock(List.class);
方式2,直接使用标签注释需要模拟的数据
1
2
@Mock
private LoginContract.View loginView;
但要注意,方式2需要在使用模拟数据前,先在类中初始化下。比如在Junit测试类的Before中进行init
1
MockitoAnnotations.initMocks(this);
Stub打桩
条件类方法,可以先申明when模拟条件,指定return的值。再之后使用中所有该条件都会按照when中逻辑进行
1
when(loginView.isActive()).thenReturn(true);
比如上述语句,一般在before中预先申明 然后后续Junit测试中,如果运行该语句时,都会返回true
断言
T verify(T).someMethodOfT 断言某个对象的某方法执行一次
和MVP结合使用进行单元测试
由于P层只涉及逻辑不涉及V,所以用这种Junit测试更好,无需run应用,瞬间测试~
Junit测试代码中不能有Android部分相关代码 所以P中也不能有 比如TextUtils.isEmpty方法,换成自己写的StringUtils.isEmpty什么的 Retrofit中如果用RxJava,则Scheduler也注意别用Android的,而且为了方便测试,最好换成同步的 比如可以添加一个全局变量区分,如果是unit测试时,使用immediate变成同步处理
1
2
3
4
5
6
7
8
9
10
11
public static <T> Observable<T> decorate(Observable<T> observable) {
Observable<T> newObservable;
if(BoreConstants.isUnitTest) {
newObservable = observable.subscribeOn(Schedulers.immediate())
.observeOn(Schedulers.immediate());
} else {
newObservable = observable.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread());
}
return newObservable;
}
MVP + Mockito 测试登录例子 如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
public class LoginPresenterTest {
private LoginPresenter presenter;
@Mock
private LoginContract.View view;
@Before
public void setupMocksAndView() {
// Mockito has a very convenient way to inject mocks by using the @Mock annotation. To
// inject the mocks in the test the initMocks method needs to be called.
MockitoAnnotations.initMocks(this);
// The presenter wont't update the view unless it's active.
when(view.isActive()).thenReturn(true);
BoreConstants.isUnitTest = true;
presenter = new LoginPresenter(view);
}
@Test
public void testLogin_EmptyPassword() throws Exception {
presenter.login("13913391521", "");
verify(view).showLocalError("密码不能为空");
}
@Test
public void testLogin_EmptyUsername() throws Exception {
presenter.login(null, "123456");
verify(view).showLocalError("用户名不能为空");
}
@Test
public void testLogin_Success() throws Exception {
String phone = "18551681236";
presenter.login(phone, "123456");
verify(view).showProgress();
verify(view).dismissProgress();
verify(view).loginSuccess(UserInfoKeeper.getCurrentUser());
Assert.assertEquals(UserInfoKeeper.getCurrentUser().getUsername(), phone);
}
@Test
public void testLogin_UserNotExit() throws Exception {
String phone = "110110110";
presenter.login(phone, "123456");
verify(view).showProgress();
verify(view).dismissProgress();
verify(view).showWebError("找不到用户");
}
@Test
public void testLogin_PswError() throws Exception {
String phone = "18551681236";
presenter.login(phone, "110119120");
verify(view).showProgress();
verify(view).dismissProgress();
verify(view).showWebError("密码不正确");
}
}
before中进行mock初始化和view的when处理 成员变量mock View对象 测试方法中先新建presenter对象(loginView已经mock出来,不会为null) 然后调用待处理逻辑方法,最后断言view执行了xx方法 常用断言有verify(object).method() 判断某个方法执行了一次 或者Assert.assertXXX的各种判断等
when的处理对象必须是mock的 Capture也是一样 mockito就是模拟测试,所以相关东西大部分都要mock的
如果想mock接口数据,就要mock httprequest对象,因此将其内部的static方法改造为对象方法 同时将httprequest对象依赖注入 这样正式请求接口就getIntence获取真实对象 模拟请求时就mock一个对象
参考资料 http://blog.csdn.net/zhangxin09/article/details/42422643