谨慎使用 @MockBean注解 我就是我 2022-12-29 02:27 512阅读 0赞 ### 目录 ### * * * 问题定位 * 参考issue * 文档释义 * 解决方案 * * 快速方案 * 相关阅读 * 其他资料 ### 问题定位 ### @MockBean或@SpyBean会改变bean在context中的状态,为了保证每个test运行时context不被污染而进行刷新,属于正常现象,是springboot的默认行为。但由此会导致其他不可预料的错误。 springboot提供了spirng-boot-starter-test以供开发者使用单元测试,其中org.springframework.boot.test的mockito包,是springboot对org.mockito的增强替换,@MockBean和@SpyBean是主要的mock注解。 tests使用@MockBean和@SpyBean会导致bean在spring test context中缓存的cache key变化,springboot默认缓存context,当顺序执行的两个tests分别依赖不同的但需要被mock的bean或者同一个bean而在其中一个test中需要被mock时(或者其他会污染context的行为),spring test context发生变化,进而引起运行第二个test前刷新context。 于是多个tests重复启动spring test context(较早的context不会自动关闭?jvm中已存在class meta?),不能保证每个tests的执行上下文的独立性、隔离性,某一些bean在被多次创建后会出现异常。 ### 参考issue ### [Spring boot test: context loaded for every test?][Spring boot test_ context loaded for every test] [Context not being reused in tests when MockBeans are used][] [Context isn’t cached when MockBean using][Context isn_t cached when MockBean using] [@SpyBean makes context restart between two tests][SpyBean makes context restart between two tests] ### 文档释义 ### [Mocking and Spying Beans][] ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d0X2JldHRlcg_size_16_color_FFFFFF_t_70] [Context Management and Caching][] ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d0X2JldHRlcg_size_16_color_FFFFFF_t_70 1] ### 解决方案 ### #### 快速方案 #### 一、对于非web工程 将用例中使用的mock变量放到父类,这样所有的用例类都使用同一个上下文,具体的mock放到实际的测试用例类上。 //测试父类 @RunWith(SpringJUnit4ClassRunner.class) public class XxxTestBase { @MockBean private BeanService1 beanService1; @MockBean private BeanService2 beanService2; @MockBean private BeanService2 beanService2; } //具体的测试类 public class ConcreteTest extends XxxTestBase { @Test public void test() { beanService2.doTest(); } } 二、对于web工程 如果是web工程,spring context刷新会导致端口冲突,通过设置端口随机来避免。 @springbootTest(class=SOFABootTestApplication.class, WebEnironment=WebEnvironment.RANDOM_PORT) 参考文档:Testing Spring Boot Applications 可能潜在的问题 1.把该bean不需要被mock的方法一起mock了(@SpyBean替换@MockBean?); 2.一些场景下该bean不需要被mock,而上下文中该bean是被mock的; 3.web工程设置为端口随机,可能会导致某些依赖端口的用例出现失败; ### 相关阅读 ### [Do You Really Need @DirtiesContext?][Do You Really Need _DirtiesContext] [Annotation Type DirtiesContext][] ### 其他资料 ### [Mockito.mock() vs @Mock vs @MockBean][Mockito.mock_ vs _Mock vs _MockBean] [Think Twice Before Using @MockBean][Think Twice Before Using _MockBean] [Spring boot test_ context loaded for every test]: https://stackoverflow.com/questions/48929585/spring-boot-test-context-loaded-for-every-test [Context not being reused in tests when MockBeans are used]: https://github.com/spring-projects/spring-boot/issues/7174 [Context isn_t cached when MockBean using]: https://github.com/spring-projects/spring-boot/issues/9511 [SpyBean makes context restart between two tests]: https://github.com/spring-projects/spring-boot/issues/12994 [Mocking and Spying Beans]: https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-testing [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d0X2JldHRlcg_size_16_color_FFFFFF_t_70]: /images/20221120/acac3abf27ce4ad9a705eec26bc65d23.png [Context Management and Caching]: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html#boot-features-testing-spring-boot-applications-mocking-beans [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d0X2JldHRlcg_size_16_color_FFFFFF_t_70 1]: /images/20221120/dcd8bb9a8b6c404ea465545a81e7c98b.png [Do You Really Need _DirtiesContext]: http://aikin.me/2018/04/02/do-you-really-need-dirties-context-annotation/ [Annotation Type DirtiesContext]: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/test/annotation/DirtiesContext.html [Mockito.mock_ vs _Mock vs _MockBean]: https://editor.csdn.net/md?articleId=111408480 [Think Twice Before Using _MockBean]: http://aikin.me/2018/03/25/think-twice-before-using-mockbean-annotation/
还没有评论,来说两句吧...