背景
之前出现过@MockBean的类在跑UT时报错,检查日志发现运行时没mock导致的报错,代码中明明是mock的,单独跑UT没问题,批量跑就出问题了,之前解决过相关问题记在代码注释中了,今天团队同学又出现了,记录下吧。
先看下面的两段代码
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = OpsApplication.class)
@WebAppConfiguration
public class RebuildInstanceSlaveControllerTest{
@MockBean
private DbinsBizInterface dbinsBizInterface;
@Autowired
private RebuildInstanceSlaveController rebuildInstanceSlaveController;
@MockBean
private DbfreeLockServiceService dbfreeLockServiceService;
@MockBean
private TaskActionBusiness taskActionBusiness;
@MockBean
private DbinsCellInterface dbinsCellInterface;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = OpsApplication.class)
@WebAppConfiguration
public class RestartInstanceControllerTest {
@MockBean
private DbinsBizInterface dbinsBizInterface;
@Autowired
private RestartInstanceController restartInstanceController;
@MockBean
private DbfreeLockServiceService dbfreeLockServiceService;
@MockBean
private TaskActionBusiness taskActionBusiness;
@MockBean
private DbinsCellInterface dbinsCellInterface;
从上面两段代码发现,@Mockbean的内容完全一致,只是各自@Autowired了相关bean,这两个UT类会共用一个spring ApplicationContext,检查共用不共用,只需在idea批量跑UT时看下就知道了,不共用的每个UT文件运行都会启动spring,如下图所示,没启动的就是共用了。
共用了的场景,RebuldInstanceSlaveControllerTest和RestartInstanceControllerTest共用了的场景:
没共用的场景如下图所示:
原理
spring test框架会缓存ApplicationContext,为了实现不相互影响,只有拥有一样的configuration的测试类才会缓存(在同一个springboot中执行)
当使用MockBean时会改变configuration,因为MockBean担心会与没Mock的地方相互影响
所以一个类的configuration=(@SpringBootTest(classes = OpsApplication.class)指定的class + MockBean 的bean)
按照上面逻辑第一段代码的configuration=(OpsApplication.class + DbinsBizInterface + DbfreeLockServiceService + TaskActionBusiness + DbinsCellInterface)
第二段代码的configureation=(OpsApplication.class + DbinsBizInterface + DbfreeLockServiceService + TaskActionBusiness + DbinsCellInterface)
可见这两个UT类的configureation相同,每次运行test都是顺序执行的,如果相邻两个test的configuration相同,则会在同一个context中执行两个test类,正好这两个又相邻,所以自然而然就在一个context中执行了
在同一个context中就会导致mock的地方可能会产生影响,批量执行时产生不可预期的报错。
解决
最好的解决办法就是不要在相同的context中执行,当然每个test类都独立context会让批量执行ut速度变慢
理解了上面的原理,只要configuration不同就好了,如何做到不同,最好的办法就是互斥,比如第一段代码里@Autowired
private RebuildInstanceSlaveController rebuildInstanceSlaveController;
如果此bean在第二段代码里不需要,那就直接mock了就好了
在第二段代码里添加如下就可以解决
@Mockbean
private RebuildInstanceSlaveController rebuildInstanceSlaveController;
当时的注释
参考:https://github.com/spring-projects/spring-boot/issues/10015