mockito使用

Posted by boredream on September 14, 2016

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