You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何Mock动态数据源测试searchQuery方法?解决空指针异常

解决ValidationData.searchQuery方法的Mockito测试方案

核心思路

通过Mock替代真实的数据库连接链路:从createConnection返回的DataSource,到Connection、PreparedStatement、ResultSet,全程Mock,完全脱离真实数据库依赖。

具体实现步骤

1. 确保createConnection方法可被Stub

如果createConnection是私有方法,优先考虑重构为protected(方便测试时Stub);若无法重构,可借助PowerMock实现私有方法Mock。如果是public/protected方法,直接用Mockito处理即可。

2. 逐层Mock数据库相关对象

从数据源到结果集,逐层Stub调用逻辑,模拟真实数据库的返回行为:

  • Mock DataSource,让createConnection返回该Mock实例
  • Mock Connection,让DataSource的getConnection()返回此Mock连接
  • Mock PreparedStatement,让Connection的prepareStatement()返回此Mock语句
  • Mock ResultSet,让PreparedStatement的executeQuery()返回预设数据的Mock结果集

3. 测试代码示例

假设目标类结构如下:

public class ValidationData {
    public String searchQuery(String sql) throws SQLException {
        DataSource dataSource = createConnection();
        try (Connection conn = dataSource.getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql);
             ResultSet rs = pstmt.executeQuery()) {
            if (rs.next()) {
                return rs.getString("target_col");
            }
            return null;
        }
    }

    protected DataSource createConnection() {
        // 真实环境中获取动态数据源的逻辑
        return null;
    }
}

对应的测试类:

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
public class ValidationDataTest {

    @Mock
    private DataSource mockDataSource;
    @Mock
    private Connection mockConn;
    @Mock
    private PreparedStatement mockPstmt;
    @Mock
    private ResultSet mockRs;

    @Test
    public void testSearchQuery() throws Exception {
        // 1. 创建目标类实例并Stub createConnection方法
        ValidationData validationData = spy(new ValidationData());
        doReturn(mockDataSource).when(validationData).createConnection();

        // 2. 逐层Stub数据库调用链
        when(mockDataSource.getConnection()).thenReturn(mockConn);
        when(mockConn.prepareStatement(anyString())).thenReturn(mockPstmt);
        when(mockPstmt.executeQuery()).thenReturn(mockRs);

        // 3. 预设ResultSet返回数据
        when(mockRs.next()).thenReturn(true);
        when(mockRs.getString("target_col")).thenReturn("expected_result");

        // 4. 执行测试方法
        String result = validationData.searchQuery("SELECT target_col FROM test_table");

        // 5. 断言结果正确性
        assertEquals("expected_result", result);

        // 6. 验证调用次数(可选,确保逻辑执行符合预期)
        verify(mockDataSource, times(1)).getConnection();
        verify(mockConn, times(1)).prepareStatement(anyString());
    }
}

4. 私有方法的特殊处理

如果createConnection是私有方法,需引入PowerMock配合Mockito:

import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import javax.sql.DataSource;
import static org.junit.Assert.assertEquals;

@RunWith(PowerMockRunner.class)
@PrepareForTest(ValidationData.class)
public class ValidationDataTest {

    @Test
    public void testSearchQueryWithPrivateMethod() throws Exception {
        ValidationData validationData = PowerMockito.spy(new ValidationData());
        DataSource mockDataSource = mock(DataSource);
        // Mock私有createConnection方法
        PowerMockito.doReturn(mockDataSource).when(validationData, "createConnection");

        // 后续Mock Connection、PreparedStatement、ResultSet逻辑同上述示例
        // ...

        String result = validationData.searchQuery("SELECT * FROM test_table");
        assertEquals("expected_result", result);
    }
}

5. 长期优化方案:重构解耦

如果频繁遇到此类测试问题,建议重构代码,将DataSource作为依赖通过构造器注入:

public class ValidationData {
    private final DataSource dataSource;

    public ValidationData(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public String searchQuery(String sql) throws SQLException {
        try (Connection conn = dataSource.getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql);
             ResultSet rs = pstmt.executeQuery()) {
            // 业务逻辑...
        }
    }
}

这种情况下测试更简洁,直接Mock DataSource注入即可,无需处理createConnection方法。

关键注意事项

  • 所有数据库相关对象必须全程Mock,避免触发真实连接逻辑
  • 针对try-with-resources语法,Mockito会自动处理资源关闭的验证,无需额外配置
  • 若需模拟多行结果集,可通过when(mockRs.next()).thenReturn(true, true, false)实现

内容的提问来源于stack exchange,提问作者Alex

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 09:35:21