SpringBoot JUnit 5 中关于@Spy、@Mock和@InjectMocks注解的使用总结


/**
 * 单元测试旨在与其他组件隔离地测试组件,并且单元测试也有一个要求:执行时间要尽可能快,因为这些测试每天可能在开发人员计算机上执行数十次。
 * <p>
 *     @Spy和@Mock单元测试执行的速度快,需要打桩的代码多----写着麻烦,执行爽(随时都能执行)
 *     @SpyBean和@MockBean单元测试执行的速度慢,需要打桩的代码少----写着省事,执行恶心(跑次单元测试就可以去吃饭了)
 * <p>
 * @Spy和@Mock的区别:
 * @Spy修饰的属性里面的方法可以按照真实情况执行,在需要的时候可以打桩模拟执行结果,使用方式是Mockito.doReturn().when()--全都执行,有需要在改。
 * @Mock修饰的属性都是null,在执行单元测试的时候每个方法都需要打桩模拟执行结果,使用方式是Mockito.when().thenReturn()--全部不执行,避免意外。 <p>
 * <p>
 * <p>
 * @Spy和@InjectMocks 的应用场景:
 * @Spy修饰的属性在通过Mockito打桩数据时,无法将要打桩的属性自动注入。
 * @InjectMocks则可以自动注入,另外@InjectMocks修饰的必须是完整的类。 例如:
 * UserServiceImpl属性如果用@Spy修饰,则在使用Mockito打桩模拟数据时,在UserServiceImpl中的userRepository属性直接就是null。
 * UserServiceImpl属性如果用@InjectMocks修饰,则在使用Mockito打桩模拟数据时,在UserServiceImpl中的userRepository属性自动注入的是mock生成的代理对象。<p>
 * <p>
 * <p>
 * @Spy、@Mock和@InjectMocks修饰属性类型: <p>
 * 抽象类需要特殊处理(userCheckStrategy = Mockito.mock ( UserCheckStrategy.class, Answers.CALLS_REAL_METHODS);)。
 * @Spy:可以自动注入到其他实现类的属性中。修饰接口不会报错,不过因为接口没有实现逻辑,所以不打桩模拟的时候,接口方法永远返回null。
 * @Mock:可以自动注入到其他实现类的属性中。用@Mock修饰的属性不做打桩模拟返回结果的情况,返回的都是null,所以无所谓修饰的属性是不是接口。
 * @InjectMocks:不可以自动注入到其他实现类的属性中。修饰接口会报错。 <p>
 * <p>
 * <p>
 * @Mock和@MockBean的直观区别: <p>
 * @SpyBean和@MockBean会启动Spring容器(即使指定了其他启动方式),并且替换Spring对应类型的bean,能够调用真实的网络请求
 * @Spy和@Mock生成的对象不受spring管理,也不会替换Spring对应类型的bean
 *
 *  <p>
 *  <p>
 * 解决线程导致单元测试无法覆盖执行
 *         // 新增对异步线程里面Runnable方法的驱动
 *         Mockito.doAnswer(
 *         (InvocationOnMock invocation) -> {
 *         ((Runnable) invocation.getArguments()[0]).run();
 *         return null;
 *         }
 *         ).when(threadPoolTaskExecutor).execute(Mockito.any(Runnable.class));
 *         // 新增对异步线程里面Runnable方法的驱动
 *         Mockito.doAnswer(
 *         (InvocationOnMock invocation) -> {
 *         ((Runnable) invocation.getArguments()[0]).run();
 *         return null;
 *         }
 *         ).when(asyncExecutor).execute(Mockito.any(Runnable.class));
 * <p>
 * 使用建议:
 * 1、默认使用@Spy,有需要打桩模拟返回结果的情况可以自定义模拟返回结果,尽可能的覆盖更多的代码逻辑。
 * 2、对依赖项全部使用@Mock,每个依赖项都打桩模拟返回结果,有遗漏的地方会报空指针异常,在测试用例的开发阶段就能识别出来,尽可能的减少依赖项对单元测试执行结果的影响。
 * 3、对于实现类中包含需要通过Mockito打桩模拟结果的依赖项属性时,使用@InjectMocks,使被@Mock修饰的属性能够自动注入到实现类中。
 * 4、@Spy修饰实现类、@InjectMocks修饰实现类、@Mock修饰接口。
 * <p>
 * <p>
 * 疑问:
 * Q:在OrderAggServiceImpl中的orderService属性如果没有手动set方式赋值的时候,orderService属性是null。
 * 而其他属性threadPoolTaskExecutor、resourceService、snowflakeIdWorker都是自动有值不为空的。区别是orderService是@InjectMocks修饰,
 * 而其他有值的属性是@Spy修饰,但是将orderService属性用@Spy和@InjectMocks同时修饰也不能自动赋值,另外这两个注解同时使用时只有离属性最近的一个注解起作用。
 * A:
 * <p>
 * Q:@Spy、@Mock和@InjectMocks三个注解的注入规则是什么?什么时候能自动注入,什么时候需要手动赋值?
 * A:
 * <p>
 *
 */


实战代码GitHub地址https://github.com/wand007/cloud-example/blob/master/example-jpa/example-client/src/test/java/com/cloud/example/jpa/client/OrderClientTest.java

参考文章:Mockito关于抽象类的问题_演员¹²¹³⁸的博客-CSDN博客_mockito 抽象类

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐