Mockito使用

  • Mock(打桩) 测试就是在测试过程中,对于某些不容易构造(如 HttpServletRequest 必须在Servlet 容器中才能构造出来)或者不容易获取比较复杂的对象(如 JDBC 中的ResultSet 对象),用一个虚拟的对象(Mock 对象)来创建以便测试的测试方法。

  • Mock(打桩) 最大的功能是帮你把单元测试的耦合分解开,如果你的代码对另一个类或者接口有依赖,它能够帮你模拟这些依赖,并帮你验证所调用的依赖的行为。

1 正常流程案例:

1
现在有个controller,在里面有个依赖service,在单元测试的时候不希望执行事务层的代码,所以需要mock掉service对象及它的行为

1.1 先创建测试类,如果有多个测试用例,可以提出父类

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
/**
* [@author](https://my.oschina.net/arthor) wisdom
*/
@RunWith(SpringRunner.class)
@SpringBootTest
/**
* @AutoConfigureMockMvc 用于自动配置MockMvc
*/
@AutoConfigureMockMvc
@Slf4j
public class TestMocktioService {

@Rule
public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation("target/generated-snippets");

@Autowired
private WebApplicationContext context;

public MockMvc mockMvc;

@Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration(this.restDocumentation)
// 预处理器,这里设置的是对请求参数和响应参数进行美化输出
.operationPreprocessors() // 预处理器
// 格式化请求或响应参数,比如json串
.withRequestDefaults(Preprocessors.prettyPrint())
.withResponseDefaults(Preprocessors.prettyPrint())).build();
}
...
}

1.2 测试

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
@Test
public void test() throws Exception{

Parameters parameters = new Parameters();
parameters.add("name","小明");
//perform,执行一个RequestBuilders请求,会自动执行SpringMVC的流程并映射到相应的控制器执行处理
MockHttpServletRequestBuilder builder = RestDocumentationRequestBuilders
.get("/test-doc/test1")
.contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)
// 乱码
.accept(MediaType.APPLICATION_JSON_UTF8_VALUE)
.params(parameters);
ResultActions perform = this.mockMvc.perform(builder);
//andExpect,添加ResultMathcers验证规则,验证控制器执行完成后结果是否正确,【这是一个断言】
perform.andExpect(MockMvcResultMatchers.status().isOk()).
andDo(MockMvcRestDocumentation.document("test",
RequestDocumentation.requestParameters(
// 请求字段
RequestDocumentation.parameterWithName("name").description("名字")),
PayloadDocumentation.responseFields(
// 响应字段
PayloadDocumentation.fieldWithPath("age").description("年龄").optional(),
PayloadDocumentation.fieldWithPath("name").description("名字").optional(),
PayloadDocumentation.fieldWithPath("sex").description("性别").optional())))
.andReturn();
}

1.3 执行package命令进行整体流程,可以看到在target配置路径下找到响应html文档, html生成的response里是小明那条结果集

2. 现在不走service层,或者这个service里某些依赖我们没有,这里需要mock service

2.1 手动mock(打桩)

  • @Mock : 为某个类创建Mock对象,(通常直接加在属性字段上)

    • 等价于:Mockito.mock(xxx.class);
  • Mockito初始化的方式

    • MockitoAnnotations.initMocks(this);
    • @RunWith(MockitoJUnitRunner.class)
    • @Rule public MockitoRule mockito = MockitoJUnit.rule()
  • 注入测试接口以及需要mock的对象

1
2
3
4
5
6
7
8
// 似于spring的依赖注入,为AlertAction自动注入mock对象
// @InjectMocks
@Autowired
TestController testController;

// 为某个类创建Mock对象
@Mock
TestService testService;
  • 在测试代码执行之前,初始化mockito并手动注入事务层对象(这里也可以在控制层添加手动注入业务层-加个set方法,@Autowired...)
1
2
3
4
5
6
7
// Mockito初始化
MockitoAnnotations.initMocks(this);
MockitoAnnotations.initMocks(testService);
// 最后走的是java映射包的set方法
ReflectionTestUtils.setField(testController, "testService", testService);


  • 这里我把这个事务层的返回结果改掉,再次重新打包
1
2
3
// 返回值(不会真正走真实方法)
Mockito.doReturn(new TestEntity("25","王先生","男")).when(testService).findByName(Mockito.anyString());
// Mockito.when(testService.findByName(Mockito.anyString())).thenReturn(new TestEntity("25","王先生","男"));
  • 可以看到结果是我mock的结果
  • mockito不仅mock了返回结果,还可以mock行为
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 当时小明时候返回第一个,否则返回下一个
Mockito.doAnswer(new Answer() {
@Override
public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
//这里可以获得传参
Object[] arguments = invocationOnMock.getArguments();
String argument = (String) arguments[0];
if (argument.equals("小明")){
return new TestEntity("25","王先生","男");
}else {
return new TestEntity("20","李先生","女");
}
}
}).when(testService).findByName(Mockito.anyString());

3. mockito还有一些函数

1
Mockito.verify(),Mockito.doThrow()....等等,用到了再看啥意思就ok

4. 还有Spy的概念(这里我没深研究)

1
2
3
4
5
6
7
@mock对比
二者的区别在于:

1、Mock声明的对象,对函数的调用均执行mock(即虚假函数),不执行真正部分。

2、Spy声明的对象,对函数的调用均执行真正部分。
...

mocktio+restdoc测试代码仓库地址