1.简介
- 配合断言使用(杜绝System.out)
- 可重复执行
- 不依赖环境
- 不会对数据产生影响
- Spring的上下文环境不是必备的
- 一般都配合mock类框架对数据库进行隔离
mock类使用场景:
要进行测试的方法存在外部依赖(DB,Redis,第三方接口),为了专注于对该方法的逻辑进行测试,就希望能隔离出来外部依赖,避免外部依赖成为单测的阻塞项,一般单测都是在service进行测试
创建mock对象的三种方法:
java">/*** 创建mock对象的第一种方法*/
@ExtendWith(MockitoExtension.class)
public class InitMockOrSpyMethod1 {@Mockprivate UserService mockUserService;@Spyprivate UserService spyUserServices;@Testpublic void test1(){System.out.println("Mockito.mockingDetails(mockUserService).isMock():"+Mockito.mockingDetails(mockUserService).isMock());System.out.println("Mockito.mockingDetails(mockUserService).isSpy():"+Mockito.mockingDetails(mockUserService).isSpy());System.out.println("Mockito.mockingDetails(spyUserServiceS).isMock():"+Mockito.mockingDetails(spyUserServices).isMock());// spy对象是另一种类型的mock对象System.out.println("Mockito.mockingDetails(spyUserServiceS).isSpy():"+Mockito.mockingDetails(spyUserServices).isSpy());}}
java">/*** 创建mock对象的第二种方法*/
public class InitMockOrSpyMethod2 {private UserService mockUserService;private UserService spyUserServices;@BeforeEachpublic void init(){mockUserService = Mockito.mock(UserService.class);spyUserServices = Mockito.spy(UserService.class);}@Testpublic void test1(){System.out.println("Mockito.mockingDetails(mockUserService).isMock():"+Mockito.mockingDetails(mockUserService).isMock());System.out.println("Mockito.mockingDetails(mockUserService).isSpy():"+Mockito.mockingDetails(mockUserService).isSpy());System.out.println("Mockito.mockingDetails(spyUserServiceS).isMock():"+Mockito.mockingDetails(spyUserServices).isMock());// spy对象是另一种类型的mock对象System.out.println("Mockito.mockingDetails(spyUserServiceS).isSpy():"+Mockito.mockingDetails(spyUserServices).isSpy());}
}
java">/*** 创建mock对象的第三种方法*/
public class InitMockOrSpyMethod3 {@Mockprivate UserService mockUserService;@Spyprivate UserService spyUserServices;@BeforeEachpublic void init(){MockitoAnnotations.openMocks(this);}@Testpublic void test1(){System.out.println("Mockito.mockingDetails(mockUserService).isMock():"+Mockito.mockingDetails(mockUserService).isMock());System.out.println("Mockito.mockingDetails(mockUserService).isSpy():"+Mockito.mockingDetails(mockUserService).isSpy());System.out.println("Mockito.mockingDetails(spyUserServiceS).isMock():"+Mockito.mockingDetails(spyUserServices).isMock());// spy对象是另一种类型的mock对象System.out.println("Mockito.mockingDetails(spyUserServiceS).isSpy():"+Mockito.mockingDetails(spyUserServices).isSpy());}
}
2.mock和spy对象
mock对象:
- 方法插桩:执行插桩逻辑
- 方法不插桩:返回mock对象的默认值
- 作用对象:类吗,接口
spy对象:
- 方法插桩:执行插桩逻辑
- 方法不插桩:调用真实方法
- 作用对象:类吗,接口
3.参数匹配
java">@Test
public void test1() {UserDO userDO = new UserDO();userDO.setId(1L);doReturn("nothing").when(mockUserService).selectNameById(userDO.getId());// 只有执行Mockito.doReturn才执行插桩System.out.println(mockUserService.selectNameById(userDO.getId()));// userDO2对象不进行插桩,返回默认值UserDO userDO2 = new UserDO();userDO2.setId(2L);System.out.println(mockUserService.selectNameById(userDO2.getId()));
}
java"> /*** 此时我只想拦截UserDO类型的任意对象* ArgumentMatchers.*拦截任意类型*/@Testpublic void test2() {doReturn("nothingName").when(mockUserService).selectNameById(ArgumentMatchers.anyLong());UserDO userDO = new UserDO();userDO.setId(1L);System.out.println(mockUserService.selectNameById(userDO.getId()));UserDO userDO2 = new UserDO();userDO2.setId(2L);System.out.println(mockUserService.selectNameById(userDO2.getId()));}
方法插桩
指定调用某个方法时的行为,达到相互隔离的效果
- 返回指定值
java">import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;@ExtendWith(MockitoExtension.class)
public class StubTest {@Mockprivate List<String> mockList;/*** 指定返回值*/@Testpublic void test1() {// 方法一doReturn("zero").when(mockList).get(0);// 如果返回值不相等,则测试不通过Assertions.assertEquals("zero", mockList.get(0));//方法二when(mockList.get(1)).thenReturn("one");Assertions.assertEquals("one", mockList.get(1));}
}
- void返回值方法插桩
java">/*** void 返回值插桩*/
@Test
public void test2() {// 调用mockList.clear()什么也没做doNothing().when(mockList).clear();mockList.clear();verify(mockList, times(1)).clear();
}
-
插桩的两种形式
- when(obj.someMethod.thenXXX):用于mock对象
- doXXX():用于mock/spy对象
区别在于不插桩时doXXX应用与spy对象时会调用真实方法
java">public void test4() {when(mockUserServiceImpl.getNumber()).thenReturn(99);System.out.println("mockUserServiceImpl.getNumber():"+mockUserServiceImpl.getNumber());doReturn(99).when(spyUserServiceImpl).getNumber();System.out.println("spyUserServiceImpl.getNumber() = " + spyUserServiceImpl.getNumber()); }
-
抛异常
java">/*** 断言异常*/@Testpublic void test3() {doThrow(RuntimeException.class).when(mockList).get(anyInt());try{mockList.get(10000);//此处get(10000)为空Assertions.fail();}catch (RuntimeException e){// 断言表达式为真Assertions.assertTrue(e instanceof RuntimeException);}}
- 多次插桩
java">/*** 多次插桩*/@Testpublic void test4() {// 第一次调用返回1,第二次返回2,第三次返回3以及以后得调用都返回三when(mockList.size()).thenReturn(1).thenReturn(2).thenReturn(3);Assertions.assertEquals(1,mockList.size());Assertions.assertEquals(2,mockList.size());Assertions.assertEquals(3,mockList.size());Assertions.assertEquals(4,mockList.size());}
- thenAnswer
java">/*** thenAnswer:实现指定的插桩逻辑*/@Testpublic void test5() {// 此处不管传入什么参数,都返回100*3when(mockList.get(anyInt())).thenAnswer(new Answer<String>() {/*** 泛型是要返回插桩的返回值类型* @param invocationOnMock* @return* @throws Throwable*/@Overridepublic String answer(InvocationOnMock invocationOnMock) throws Throwable {Integer argument = invocationOnMock.getArgument(0, Integer.class);return String.valueOf(argument*100);}});String s = mockList.get(3);Assertions.assertEquals("300",s);}
- 执行真正的原始方法
java">/*** 执行真正的原始方法*/@Testpublic void test6() {when(mockUserServiceImpl.getNumber()).thenCallRealMethod();int number = mockUserServiceImpl.getNumber();Assertions.assertEquals(1,number);}
- verify的使用
java"> /*** 指定方法返回几次*/@Testpublic void test3() {mockUserService.add("lily", "123123", new ArrayList<>());// 验证add方法被调用了1次Mockito.verify(mockUserService, Mockito.times(1)).add("lily", "123123", new ArrayList<>());// 要么全部使用ArgumentMatchers,不能一半使用参数,一半使用ArgumentMatchersMockito.verify(mockUserService, Mockito.times(1)).add(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyList());}
4.@InjectMocks注解
- 作用:如果@InjectMocks声明的变量需要用到mock,spy对象,mockito会自动使用当前类里的mock或者spy成员进行an类型或者名字注入
- 原理:构造器注入,setter注入,字段反射注入
java">@ExtendWith(MockitoExtension.class)
public class InjectMocksTest {/*** 被InjectMocks标注的属性,必须是实现类,因为Mockito会创建InjectMocks注解的类的实例,* 并注入到被InjectMocks标注的属性中,默认创建的对象就是没有经过Mockito处理过的对象,因此* 配合@Spy注解,变成可以调用默认方法的mock对象*/@InjectMocks@Spyprivate UserServiceImpl userService;@Mockprivate UserFeatureService userFeatureService;@Testpublic void test() {int number = userService.getNumber();Assertions.assertEquals(1, number);}
}