Mockito测试套件执行报错:当前线程已注册静态模拟,单独运行正常
代码示例
@RunWith(MockitoJUnitRunner.class) public class MyTest { private AutoCloseable autoCloseable; @BeforeEach void init() throws InterruptedException { autoCloseable = MockitoAnnotations.openMocks(this); } @AfterEach public void tearDown() throws Exception { autoCloseeable.close(); // 注意:此处存在拼写错误,应为autoCloseable } }
测试中使用Mockito.mockConstruction模拟内部对象创建,运行测试套件时抛出异常:
org.mockito.exceptions.base.MockitoException: For
com.stocktrader.service.ib.ltp.GetStockOrFuturesPrice, static mocking
is already registered in the current threadTo create a new mock, the existing static mock registration must be
deregistered
单独运行该测试正常,套件运行失败。
原因
mockConstruction是Mockito用于模拟构造函数的API,它的模拟注册是绑定到当前线程的。当运行测试套件时,JUnit通常会复用线程池中的线程,上一个测试用例中通过mockConstruction创建的模拟没有被正确关闭,导致线程上残留了未注销的静态模拟注册,下一个测试再尝试创建同类型的构造模拟时就会触发冲突异常。
而单独运行测试时,测试结束后线程直接销毁,残留的注册信息也随之消失,因此不会报错。
另外你现有代码中的@AfterEach方法存在拼写错误:autoCloseeable应为autoCloseable,这会导致普通Mock的资源也无法正常关闭,但这不是本次异常的直接原因。
解决办法
方法1:使用try-with-resources自动关闭构造模拟
在使用mockConstruction的测试方法中,用try-with-resources语法包裹,确保模拟资源在测试结束后自动关闭:
@Test void testInternalObjectCreation() { // 用try-with-resources管理mockConstruction的资源 try (var constructionMock = Mockito.mockConstruction(GetStockOrFuturesPrice.class, (mock, context) -> { // 配置mock的行为,比如指定方法返回值 when(mock.getPrice()).thenReturn(new BigDecimal("100.0")); })) { // 执行被测方法 // 断言验证逻辑 } }
方法2:统一在@AfterEach中关闭构造模拟
如果多个测试用例都需要使用构造模拟,可以在测试类中新增变量保存mockConstruction返回的AutoCloseable,然后在@AfterEach中统一关闭:
@RunWith(MockitoJUnitRunner.class) public class MyTest { private AutoCloseable autoCloseable; private AutoCloseable constructionCloseable; // 新增:保存构造模拟的资源 @BeforeEach void init() throws InterruptedException { autoCloseable = MockitoAnnotations.openMocks(this); } @AfterEach public void tearDown() throws Exception { // 先关闭构造模拟的资源 if (constructionCloseable != null) { constructionCloseable.close(); constructionCloseable = null; // 重置为null,避免影响下一个测试 } // 关闭普通Mock的资源(修正拼写错误) autoCloseable.close(); } @Test void testWithMockConstruction() { // 保存构造模拟的资源 constructionCloseable = Mockito.mockConstruction(GetStockOrFuturesPrice.class, (mock, context) -> { // 配置mock行为 }); // 测试逻辑 } }
内容的提问来源于stack exchange,提问作者Kannan J

