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

如何对使用ResultSet和PreparedStatement的方法进行单元测试

问题原因

你触发空指针的核心原因是DataBaseService内部依赖的connection对象没有被mock和注入,你当前只mock了ResultSet,甚至直接mock了DataBaseService本身再调用真实方法,服务内部用到的connection、PreparedStatement都是空值,调用connection.prepareStatement时自然会抛出空指针异常。

正确的单元测试编写逻辑
  • 不要直接mock你要测试的业务类本身,只需要mock它的所有外部依赖即可,你要验证的就是业务类的真实代码逻辑
  • 梳理清楚方法内部的调用链路,按顺序给每个mock依赖定义对应入参的返回值,不要遗漏任何一步调用
  • 涉及结果集循环的场景,要匹配真实的迭代逻辑模拟next()方法的返回值
修正后的测试代码示例
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.test.util.ReflectionTestUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.mockito.Mockito.*;

public class DataBaseServiceTest {
    // 要测试的是真实业务类,不要mock
    private DataBaseService dataBaseService;
    // mock所有外部依赖对象
    private Connection connection;
    private PreparedStatement preparedStatement;
    private ResultSet resultSet;

    @BeforeEach
    public void initMock() {
        connection = mock(Connection.class);
        preparedStatement = mock(PreparedStatement.class);
        resultSet = mock(ResultSet.class);
        dataBaseService = new DataBaseService();
        // 注入mock的connection到业务类,根据你实际的注入方式调整
        // 如果有构造方法注入可以直接new DataBaseService(connection)
        // 如果是私有字段无setter用反射注入:
        ReflectionTestUtils.setField(dataBaseService, "connection", connection);
    }

    @Test
    public void getLastIdIsCorrect() throws SQLException {
        // given
        int expectedId = 1;
        String matchQuery = "select 3537340;";
        // 按方法调用顺序定义mock返回
        when(connection.prepareStatement(matchQuery)).thenReturn(preparedStatement);
        when(preparedStatement.executeQuery()).thenReturn(resultSet);
        // 匹配while循环逻辑:第一次next返回true读取数据,第二次返回false结束循环
        when(resultSet.next()).thenReturn(true).thenReturn(false);
        when(resultSet.getInt("3537340")).thenReturn(expectedId);

        // when
        int result = dataBaseService.getLastId();

        // then
        assertEquals(expectedId, result);
        assertNotEquals(101, result);
        // 可选:验证依赖调用符合预期,避免逻辑写错但返回值刚好匹配的情况
        verify(connection).prepareStatement(matchQuery);
        verify(preparedStatement).executeQuery();
        verify(resultSet, times(2)).next();
        verify(resultSet).getInt("3537340");
    }
}
同类数据库操作方法通用测试方案

所有涉及JDBC操作的方法都可以套用这个逻辑:

  1. 先找出方法依赖的所有外部资源:Connection、PreparedStatement、ResultSet,全部做mock
  2. 按照方法内部的执行顺序,依次定义每个mock对象的入参匹配规则和返回值
  3. 针对分支场景分别写测试用例,比如结果集为空的场景、SQL执行抛异常的场景都可以单独覆盖
  4. 必要时添加verify验证调用次数、入参是否符合预期,提高测试准确性

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 18:27:06