最近公司要求补充测试用例,正好借这个机会学习下mockito,记录下以便加深记忆。
以下测试都是用的
testng
踩坑记录
1. 明明打桩了却不起作用
- 如果你的test横跨了多个测试文件,检查是否有 static 的容器。
1. 基本使用
public class MyTest {
@Test
public void test() {
final MockTest mockTest = Mockito.mock(MockTest.class);
when(mockTest.get()).thenReturn("b");
assertEquals(mockTest.get(), "b");
}
@Test
public void test2() {
assertNull(mockTest.get());
}
public static class MockTest {
public String get() {
return "a";
}
}
}
2. 注解
使用注解必须在使用前调用 MockitoAnnotations.openMocks(this);, 该方法最后也会调用 Mockito 中的方法。相关类 MockAnnotationProcessor SpyAnnotationEngine等。
2.1. mockito 自带注解
1. @Mock-mock 一个类,当调用该类中的方法时,都不会产生实际的调用
public class MyTest {
@Mock
private MockTest mockTest;
@BeforeClass
public void before(){
MockitoAnnotations.openMocks(this);
}
@Test
public void test() {
when(mockTest.get()).thenReturn("b");
assertEquals(mockTest.get(), "b");
}
public static class MockTest {
public String get() {
return "a";
}
}
}
2. @Spy - 和 @Mock 类似,但是正好相反,该类的方法就会产生实际的调用
public class MyTest1 {
@Spy
private MockTest mockTest;
@BeforeClass
public void before() {
MockitoAnnotations.openMocks(this);
}
@Test
public void test() {
assertEquals(mockTest.get(), "a");
}
@Test
public void test2() {
when(mockTest.get()).thenReturn("b");
assertEquals(mockTest.get(), "b");
}
public static class MockTest {
public String get() {
return "a";
}
}
}
3. @InjectMocks - 产生一个真实的实例,并且会注入 @Mock 和 @Spy 的类。
public class MyTest2 {
@InjectMocks
private MockTest mockTest;
@Mock // 该类会被注入到 mockTest
private InjectMockObj injectMockObj;
@Spy // 该类会被注入到 mockTest
private InjectSpybj injectSpybj;
@BeforeClass
public void before() {
MockitoAnnotations.openMocks(this);
}
@Test
public void test() {
assertNull(mockTest.get());
}
@Test
public void test2() {
when(injectMockObj.get()).thenReturn("a");
assertEquals(mockTest.get(), "a");
}
@Test
public void test3() {
assertEquals(mockTest.getSpy(), "a");
}
public static class MockTest {
private InjectMockObj injectMockObj;
private InjectSpybj injectSpybj;
public String get() {
return injectMockObj.get();
}
public String getSpy() {
return injectSpybj.get();
}
}
public static class InjectMockObj {
public String get() {
return "a";
}
}
public static class InjectSpybj {
public String get() {
return "a";
}
}
}
4. @Captor
配合 verify 捕获参数。
public class MyTest3 {
@Mock
private MockTest mockTest;
@Captor
private ArgumentCaptor<String> captor;
private AutoCloseable closeable;
@BeforeClass
public void open() {
closeable = MockitoAnnotations.openMocks(this);
}
@AfterClass
public void release() throws Exception {
closeable.close();
}
@Test
public void shouldDoSomethingUseful() {
mockTest.set("b");
verify(mockTest).set(captor.capture());
assertEquals("b", captor.getValue());
}
public static class MockTest {
public String set(String a) {
return "a";
}
}
}
2.2. Spring Boot 中的注解
@MockBean @SpyBean
和 @Mock @Spy 类似,只是多了一个步骤,会把生成的类替换掉 ApplicationContext 中的类。如果同一个类型有多个类可以搭配 @Qualifier 注解使用。
3. 常用方法
when(..).thenReturn(..)doReturn(..).doReturn().when(..).doStuff()
注意在一个方法上可以打两个桩然后有不同的操作,当调用时也会有不同的操作
public class MyTest4 {
@Mock
private MockTest mockTest;
@BeforeClass
public void before(){
MockitoAnnotations.openMocks(this);
}
@Test
public void test() {
doReturn("b").doReturn("z").when(mockTest).get();
assertEquals(mockTest.get(), "b");
assertEquals(mockTest.get(), "z");
}
@Test
public void test1() {
assertNull(mockTest.get());
}
public static class MockTest {
public String get() {
return "a";
}
}
}
- 如何同一个方法多个调用返回不同结果
Mockito.when(XXXXXX)
.thenReturn(XXXXX) // 第一次调用时返回的结果
.thenReturn(XXXXX) // 第二次调用时返回的结果
4. 参数匹配
只有当调用的方法的参数和类型和数量匹配时才能触发打桩点。
ArgumentMatchers.any()匹配任何参数ArgumentMatchers.anyString()匹配任务字符串ArgumentMatchers.anyInt()匹配任何Integer值ArgumentMatchers.eq()匹配某个值- 等等
5. 总结
在使用了几天的 Mockito 之后,我的感受:
- 可以自定义的控制外部依赖对于测试的影响,当外部请求比较复杂时,可以使用 Mockito 来 mock 数据,而无需真正的去调用外部请求。
- 只要你愿意,测试覆盖率可以非常高
题外话: 如果配合 testContainer 那就是无敌