<commit_before>---
layout: post
title: '安卓单元测试（九）：使用Mockito Annotation快速创建Mock'
date: 2016-07-16T13:22:13+08:00
comments: true
post_id: mockito-annotation
---

注：
如果你还不了解Mock的概念或Mockito框架的使用，请先看[这篇文章](http://chriszou.com/2016/04/29/android-unit-testing-mockito.html)。  


### @Mock的基本用法
如果你follow了这个安卓单元测试系列文章，那么到现在为止，你应该很清楚mock的概念和使用了，创建Mock的方法我们都知道：

```java
YourClass yourInstance = Mockito.mock(YourClass.class);
```

比如：

```java
public class LoginPresenterTest {

    @Test
    public void testLogin() {
        UserManager mockUserManager = mock(UserManager.class);
        PasswordValidator mockValidator = mock(PasswordValidator.class);
        Mockito.when(mockValidator.verifyPassword("<PASSWORD>")).thenReturn(true);

        LoginPresenter presenter = new LoginPresenter(mockUserManager, mockValidator);

        presenter.login("xiaochuang", "xiaochuang is handsome");

        verify(mockUserManager).performLogin("xiaochuang", "xiaochuang is handsome");
    }
}
```

虽然很简单，但是如果一个测试类里面很多测试方法都要用到mock，那写起来就会有点麻烦，这时候我们可以写一个`@Before`方法来作这个setup工作：

```java
public class LoginPresenterTest {

    UserManager mockUserManager;
    PasswordValidator mockValidator;
    LoginPresenter loginPresenter;

    @Before
    public void setup() {
        mockUserManager = mock(UserManager.class);
        mockValidator = mock(PasswordValidator.class);
        loginPresenter = new LoginPresenter(mockUserManager, mockValidator);
    }

    @Test
    @JSpec(desc = "should ")
    public void testLogin() {
        Mockito.when(mockValidator.verifyPassword("<PASSWORD>")).thenReturn(true);
        loginPresenter.login("xiaochuang", "xiaochuang is handsome");

        verify(mockUserManager).performLogin("xiaochuang", "xiaochuang is handsome");
    }
}
```

这样可以部分上减少Mock的创建，然而Mock写多了，你也会觉得有点烦，因为完全是Boilerplate code。这里有个更简便的方法，那就是结合Mockito的Annotation和[JUnit Rule](http://chriszou.com/2016/07/09/junit-rule.html)，达到以下的效果：

```java
public class LoginPresenterTest {

    @Rule
    public MockitoRule mockitoRule = MockitoJUnit.rule();

    @Mock
    UserManager mockUserManager;
    
    @Mock
    PasswordValidator mockValidator;

    LoginPresenter loginPresenter;

    @Before
    public void setup() {
        loginPresenter = new LoginPresenter(mockUserManager, mockValidator);
    }

    @Test
    public void testLogin() {
        Mockito.when(mockValidator.verifyPassword("<PASSWORD>")).thenReturn(true);
        loginPresenter.login("xiaochuang", "xiaochuang is handsome");

        verify(mockUserManager).performLogin("xiaochuang", "xiaochuang is handsome");
    }
}
```

也就是说，在测试方法运行之前，自动把用`@Mock`标注过的field实例化成Mock对象，这样在测试方法里面就可以直接用了，而不用手动通过`Mockito.mock(YourClass.class)`的方法来创建。这个实现化的过程是在`MockitoRule`这个Rule里面进行的。我们知道一个JUnit Rule会在每个测试方法运行之前执行一些代码，而这个Rule实现的效果就是在每个测试方法运行之前将这个类的用了`@Mock`修饰过的field初始化成mock对象。如果你去看这个Rule的源代码的话，其实重点就在一行代码：<commit_msg>Small refinement to mockito annotation post<commit_after>---
layout: post
title: '安卓单元测试（九）：使用Mockito Annotation快速创建Mock'
date: 2016-07-16T13:22:13+08:00
comments: true
post_id: mockito-annotation
---

注：
如果你还不了解Mock的概念或Mockito框架的使用，请先看[这篇文章](http://chriszou.com/2016/04/29/android-unit-testing-mockito.html)。  


### @Mock的基本用法
如果你follow了这个安卓单元测试系列文章，那么到现在为止，你应该很清楚mock的概念和使用了，创建Mock的方法我们都知道：

```java
YourClass yourInstance = Mockito.mock(YourClass.class);
```

比如：

```java
public class LoginPresenterTest {

    @Test
    public void testLogin() {
        UserManager mockUserManager = mock(UserManager.class);
        PasswordValidator mockValidator = mock(PasswordValidator.class);
        Mockito.when(mockValidator.verifyPassword("<PASSWORD>")).thenReturn(true);

        LoginPresenter presenter = new LoginPresenter(mockUserManager, mockValidator);

        presenter.login("xiaochuang", "xiaochuang is handsome");

        verify(mockUserManager).performLogin("xiaochuang", "xiaochuang is handsome");
    }
}
```

虽然很简单，但是如果一个测试类里面很多测试方法都要用到mock，那写起来就会有点麻烦，这时候我们可以写一个`@Before`方法来作这个setup工作：

```java
public class LoginPresenterTest {

    UserManager mockUserManager;
    PasswordValidator mockValidator;
    LoginPresenter loginPresenter;

    @Before
    public void setup() {
        mockUserManager = mock(UserManager.class);
        mockValidator = mock(PasswordValidator.class);
        loginPresenter = new LoginPresenter(mockUserManager, mockValidator);
    }

    @Test
    public void testLogin() {
        Mockito.when(mockValidator.verifyPassword("<PASSWORD>")).thenReturn(true);
        loginPresenter.login("xiaochuang", "<PASSWORD>");

        verify(mockUserManager).performLogin("xiaochuang", "xiaochuang is handsome");
    }
}
```

这样可以部分上减少Mock的创建，然而Mock写多了，你也会觉得有点烦，因为完全是Boilerplate code。这里有个更简便的方法，那就是结合Mockito的Annotation和[JUnit Rule](http://chriszou.com/2016/07/09/junit-rule.html)，达到以下的效果：

```java
public class LoginPresenterTest {

    @Rule
    public MockitoRule mockitoRule = MockitoJUnit.rule();

    @Mock
    UserManager mockUserManager;

    @Mock
    PasswordValidator mockValidator;

    LoginPresenter loginPresenter;

    @Before
    public void setup() {
        loginPresenter = new LoginPresenter(mockUserManager, mockValidator);
    }

    @Test
    public void testLogin() {
        Mockito.when(mockValidator.verifyPassword("<PASSWORD>")).thenReturn(true);
        loginPresenter.login("xiaochuang", "<PASSWORD>");

        verify(mockUserManager).performLogin("xiaochuang", "xiaochuang is handsome");
    }
}
```

也就是说，在测试方法运行之前，自动把用`@Mock`标注过的field实例化成Mock对象，这样在测试方法里面就可以直接用了，而不用手动通过`Mockito.mock(YourClass.class)`的方法来创建。这个实现化的过程是在`MockitoRule`这个Rule里面进行的。我们知道一个JUnit Rule会在每个测试方法运行之前执行一些代码，而这个Rule实现的效果就是在每个测试方法运行之前将这个类的用了`@Mock`修饰过的field初始化成mock对象。如果你去看这个Rule的源代码的话，其实重点就在一行代码：
{'fraction_non_alphanumeric': 0.11343283582089553, 'fraction_numerical': 0.011940298507462687, 'mean_word_length': 5.008438818565401, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6525910', 'n_tokens_mistral': 2361, 'n_tokens_neox': 2319, 'n_words': 258}