使用MockitoAnnotations.openMocks()@BeforeClass初始化的字段在同类别测试中为null
问题根源与解决方案
核心问题分析
- JUnit 4生命周期误用:
@BeforeClass注解的方法必须是静态方法,且只会在类加载时执行一次。你的代码中@BeforeClass修饰的是非静态方法,导致初始化操作仅在某个测试类实例上执行,但JUnit 4会为每个@Test方法创建新的测试类实例——测试方法中拿到的是未初始化的新storedProcedure对象,而非@BeforeClass中初始化的那个。 - Mockito新旧API差异:
MockitoAnnotations.initMocks(this):旧API,直接将mock注入目标字段,无资源自动清理机制,mock状态会持续到测试结束。MockitoAnnotations.openMocks(this):新API,返回AutoCloseable实例,调用close()可重置mock并释放资源,需配合JUnit生命周期管理资源,否则易出现状态异常。
修复方案
方案一:使用实例级生命周期(推荐)
将类级别的@BeforeClass改为实例级的@Before,确保每个测试方法执行前都重新初始化mock和storedProcedure,避免测试间状态污染。
修改后的BaseMocks类
public abstract class BaseMocks { @Mock protected RowMapper<Object> mockRowMapper; @Mock protected Connection mockConnection; @Mock protected CallableStatement mockStatement; @Mock protected DataSourceProvider dsProvider; @Mock public DataSource mockDataSource; final MockResultSet mockResultSet = new MockResultSet(""); private AutoCloseable closeable; @Before protected void openMocks() throws Exception { closeable = MockitoAnnotations.openMocks(this); Mockito.when(dsProvider.getDatasource(Mockito.anyString())).thenReturn(mockDataSource); Mockito.when(mockDataSource.getConnection()).thenReturn(mockConnection); Mockito.when(mockConnection.prepareCall(Mockito.anyString())).thenReturn(mockStatement); Mockito.when(mockStatement.getResultSet()).thenReturn(mockResultSet); } @After protected void closeMocks() throws Exception { if (closeable != null) { closeable.close(); } } @BeforeMethod protected void resetMocks() throws SQLException { Mockito.reset(mockRowMapper); Mockito.when(mockStatement.getMoreResults()).thenReturn(true).thenReturn(false); } }
修改后的AitGetMyDetailsTest类
public class AitGetMyDetailsTest extends BaseMocks { @InjectMocks private AitGetMyDetails storedProcedure; @Before public void setup() throws Exception { super.openMocks(); storedProcedure.afterPropertiesSet(); } @Test public void myTest() throws Exception { MyObj mockMyObj = Mockito.mock(MyObj.class); storedProcedure.execute(mockMyObj, 67,45); } }
方案二:类级别初始化(不推荐,易导致测试间状态污染)
若必须使用类级别初始化,需将所有mock字段、storedProcedure改为静态,并将@BeforeClass方法改为静态。
修改后的BaseMocks类
public abstract class BaseMocks { @Mock protected static RowMapper<Object> mockRowMapper; @Mock protected static Connection mockConnection; @Mock protected static CallableStatement mockStatement; @Mock protected static DataSourceProvider dsProvider; @Mock public static DataSource mockDataSource; static final MockResultSet mockResultSet = new MockResultSet(""); private static AutoCloseable closeable; @BeforeClass public static void openMocks() throws Exception { closeable = MockitoAnnotations.openMocks(BaseMocks.class); Mockito.when(dsProvider.getDatasource(Mockito.anyString())).thenReturn(mockDataSource); Mockito.when(mockDataSource.getConnection()).thenReturn(mockConnection); Mockito.when(mockConnection.prepareCall(Mockito.anyString())).thenReturn(mockStatement); Mockito.when(mockStatement.getResultSet()).thenReturn(mockResultSet); } @AfterClass public static void closeMocks() throws Exception { if (closeable != null) { closeable.close(); } } @BeforeMethod protected void resetMocks() throws SQLException { Mockito.reset(mockRowMapper); Mockito.when(mockStatement.getMoreResults()).thenReturn(true).thenReturn(false); } }
修改后的AitGetMyDetailsTest类
public class AitGetMyDetailsTest extends BaseMocks { @InjectMocks private static AitGetMyDetails storedProcedure; @BeforeClass public static void setup() throws Exception { openMocks(); storedProcedure.afterPropertiesSet(); } @Test public void myTest() throws Exception { MyObj mockMyObj = Mockito.mock(MyObj.class); storedProcedure.execute(mockMyObj, 67,45); } }
内容的提问来源于stack exchange,提问作者user7
相关产品推荐
相关产品推荐

