如何在JUnit 5 + Mockito 5中Mock DataSource?遗留DAO测试遇阻
问题分析
你的DAO类存在两个关键测试阻碍:
ds是静态字段,且getData()每次调用都会执行getDataSource(),重新创建XmlBeanFactory并覆盖ds的值- 之前的Mock用法存在逻辑错误:
MockedConstruction的配置时机不对,导致获取mock实例时列表为空抛出索引越界- 反射注入时误操作了实例字段,而非静态字段,且后续被
getDataSource()覆盖
解决方案
方案1:正确拦截XmlBeanFactory的构造与行为
利用Mockito 5的MockedConstruction在XmlBeanFactory实例创建时,直接配置getBean()返回mock的DataSource,确保getDataSource()能拿到预期的mock对象。
import org.junit.jupiter.api.Test; import org.mockito.MockedConstruction; import javax.sql.DataSource; import java.sql.Connection; import java.sql.PreparedStatement; import static org.mockito.Mockito.*; class DAOTest { @Test void testGetData() throws Exception { // 准备Mock组件 DataSource mockDs = mock(DataSource.class); Connection mockConn = mock(Connection.class); PreparedStatement mockPst = mock(PreparedStatement.class); // 配置Mock行为 when(mockDs.getConnection()).thenReturn(mockConn); when(mockConn.prepareStatement(anyString())).thenReturn(mockPst); // 拦截XmlBeanFactory构造,实时配置getBean返回值 try (MockedConstruction<XmlBeanFactory> factoryMock = mockConstruction(XmlBeanFactory.class, (mockFactory, context) -> { when(mockFactory.getBean("dataSourceString")).thenReturn(mockDs); })) { // 调用目标方法 DAO.getData("some_input_data"); // 验证行为是否符合预期 verify(factoryMock.constructed().get(0)).getBean("dataSourceString"); verify(mockDs).getConnection(); verify(mockConn).prepareStatement("select * from sometable"); } } }
方案2:Mock静态方法+反射设置静态字段
通过Mockito 5的mockStatic拦截getDataSource()的执行逻辑,避免它覆盖我们注入的mock DataSource,再通过反射直接设置静态字段ds。
import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import java.lang.reflect.Field; import javax.sql.DataSource; import java.sql.Connection; import java.sql.PreparedStatement; import static org.mockito.Mockito.*; class DAOTest { @Test void testGetData() throws Exception { // 准备Mock组件 DataSource mockDs = mock(DataSource.class); Connection mockConn = mock(Connection.class); PreparedStatement mockPst = mock(PreparedStatement.class); // 配置Mock行为 when(mockDs.getConnection()).thenReturn(mockConn); when(mockConn.prepareStatement(anyString())).thenReturn(mockPst); // 拦截DAO的静态方法,阻止getDataSource()执行原有逻辑 try (MockedStatic<DAO> daoStaticMock = mockStatic(DAO.class)) { // 反射设置静态字段ds(静态字段用null作为set方法的实例参数) Field dsField = DAO.class.getDeclaredField("ds"); dsField.setAccessible(true); dsField.set(null, mockDs); // 调用目标方法 DAO.getData("some_input_data"); // 验证行为 verify(mockDs).getConnection(); verify(mockConn).prepareStatement("select * from sometable"); // 可选:验证getDataSource()未被执行 daoStaticMock.verify(DAO::getDataSource, never()); } } }
失败原因复盘
- 尝试1失败:你在
mockConstruction创建后才去获取constructed().get(0),但此时XmlBeanFactory还未被getDataSource()实例化,导致列表为空抛出索引越界。正确做法是在构造回调中实时配置mock行为。 - 尝试2失败:你给
dao实例设置字段,但ds是静态字段,属于类而非实例,因此parentService.set(dao, mockDataSource)无效;且后续getDataSource()执行后会重新覆盖ds的值为null。
内容的提问来源于stack exchange,提问作者Happy
相关产品推荐
相关产品推荐

