谨慎使用 @MockBean注解

问题定位

@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?
Context not being reused in tests when MockBeans are used
Context isn’t cached when MockBean using
@SpyBean makes context restart between two tests

文档释义

Mocking and Spying Beans
在这里插入图片描述
Context Management and Caching
在这里插入图片描述

解决方案

快速方案

一、对于非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?

Annotation Type DirtiesContext

其他资料

Mockito.mock() vs @Mock vs @MockBean
Think Twice Before Using @MockBean

猜你喜欢

转载自blog.csdn.net/wt_better/article/details/111408480
今日推荐