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

JUnit5+Mockito测试PostgreSQL JDBC连接遇驱动错误求助

问题:JUnit5+Mockito测试Postgres连接类时遇到驱动错误

背景

我正在用JUnit5和Mockito编写单元测试,还在学习测试方法。要测试的是通过JDBC建立PostgreSQL连接的PostgresConnection类,不想在测试中暴露真实凭证,打算用模拟凭证。

环境信息

  • JDK 17
  • Postgres依赖版本42.7.2,scope为compile

待测试类(SUT)

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class PostgresConnection {

    private static final Logger LOGGER = LogManager.getLogger(PostgresConnection.class);
    final String DATABASE ;
    final String USER;
    final String PASSWORD;

    public PostgresConnection() {
        this.DATABASE = ApplicationProperties.getPostgresDatabase();
        this.USER = ApplicationProperties.getPostgresUsername();
        this.PASSWORD = ApplicationProperties.getPostgresPassword();
    }

    public Connection connectionToDatabase () {

        Connection connection = null;

        try {
            connection = DriverManager.getConnection("jdbc:postgresql://localhost:5432/" + DATABASE, USER, PASSWORD);

            if(connection != null) {
               LOGGER.info("Connected to Postgres database");
            } else {
                LOGGER.error("Connection to Postgres database failed");
            }
        } catch(SQLException e) {

            LOGGER.error("Error " + e.getMessage());
        }

        return connection;
    }
}

测试类

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
class PostgresConnectionTest {

    @Mock
    Connection mockConnection;

    @Mock
    Statement mockStatement;

    @BeforeEach
    public void setUp() throws ClassNotFoundException, SQLException {
        Class.forName("org.postgresql.Driver");

        // Mock the Connection and configure its behavior
        mockConnection = Mockito.mock(Connection.class);
        when(mockConnection.createStatement()).thenReturn(mockStatement);

        // Configure mock behavior for DriverManager.getConnection() to return the mockConnection
        when(DriverManager.getConnection(Mockito.anyString(), Mockito.anyString(), Mockito.anyString()))
                .thenReturn(mockConnection);
    }

    @Test
    public void test_postgresDatabaseConnection() throws SQLException {
        // Create PostgresConnection instance
        PostgresConnection postgresConnection = new PostgresConnection();
        Connection connection = postgresConnection.connectionToDatabase();

        assertNotNull(connection); // Assert that connection is not null
    }
}

遇到的错误

java.sql.SQLException: No suitable driver found for 

    at java.sql/java.sql.DriverManager.getConnection(DriverManager.java:706)
    at java.sql/java.sql.DriverManager.getConnection(DriverManager.java:229)
    at ***.utils.PostgresConnectionTest.setUp(PostgresConnectionTest.java:46)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)

解决方案

问题根源

  1. Mockito默认不支持静态方法Mock:DriverManager.getConnection是静态方法,你当前的写法无法mock这个方法,导致实际调用了真实的JDBC连接逻辑。加上测试时ApplicationProperties未配置模拟值,生成的JDBC URL不完整,触发驱动找不到的错误。
  2. 依赖与测试逻辑耦合:待测试类直接依赖ApplicationProperties静态方法,导致测试必须mock静态类,增加了复杂度。

修复步骤

1. 添加Mockito-inline依赖支持静态方法Mock

Mockito从3.4.0版本开始支持inline模式mock静态方法,需要在测试依赖中添加:
如果用Maven,在pom.xml中加入:

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-inline</artifactId>
    <version>4.11.0</version> <!-- 选择与JUnit5兼容的版本 -->
    <scope>test</scope>
</dependency>

2. 修改测试类,正确Mock静态方法

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.MockedStatic;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
class PostgresConnectionTest {

    @Mock
    Connection mockConnection;

    @Test
    public void test_postgresDatabaseConnection() throws SQLException {
        // 1. Mock ApplicationProperties返回模拟凭证
        try (MockedStatic<ApplicationProperties> mockedProps = mockStatic(ApplicationProperties.class)) {
            mockedProps.when(ApplicationProperties::getPostgresDatabase).thenReturn("test_db");
            mockedProps.when(ApplicationProperties::getPostgresUsername).thenReturn("test_user");
            mockedProps.when(ApplicationProperties::getPostgresPassword).thenReturn("test_pass");

            // 2. Mock DriverManager的静态方法
            try (MockedStatic<DriverManager> mockedDriver = mockStatic(DriverManager.class)) {
                String expectedUrl = "jdbc:postgresql://localhost:5432/test_db";
                mockedDriver.when(() -> DriverManager.getConnection(expectedUrl, "test_user", "test_pass"))
                        .thenReturn(mockConnection);

                // 3. 执行测试逻辑
                PostgresConnection postgresConn = new PostgresConnection();
                Connection resultConn = postgresConn.connectionToDatabase();

                // 4. 断言与验证
                assertNotNull(resultConn);
                mockedDriver.verify(() -> DriverManager.getConnection(expectedUrl, "test_user", "test_pass"), times(1));
            }
        }
    }
}

3. 优化待测试类,降低耦合度(可选但推荐)

重构PostgresConnection,通过构造函数注入凭证参数,避免直接依赖静态类,让测试更简单:

public class PostgresConnection {

    private static final Logger LOGGER = LogManager.getLogger(PostgresConnection.class);
    private final String database;
    private final String user;
    private final String password;

    // 供测试用的带参构造函数
    public PostgresConnection(String database, String user, String password) {
        this.database = database;
        this.user = user;
        this.password = password;
    }

    // 供业务代码用的无参构造函数
    public PostgresConnection() {
        this(ApplicationProperties.getPostgresDatabase(),
             ApplicationProperties.getPostgresUsername(),
             ApplicationProperties.getPostgresPassword());
    }

    public Connection connectionToDatabase () {
        Connection connection = null;
        try {
            connection = DriverManager.getConnection("jdbc:postgresql://localhost:5432/" + database, user, password);
            if(connection != null) {
               LOGGER.info("Connected to Postgres database");
            } else {
                LOGGER.error("Connection to Postgres database failed");
            }
        } catch(SQLException e) {
            LOGGER.error("Error " + e.getMessage());
        }
        return connection;
    }
}

重构后测试类可以简化,无需mockApplicationProperties:

@Test
public void test_postgresDatabaseConnection() throws SQLException {
    String testDb = "test_db";
    String testUser = "test_user";
    String testPass = "test_pass";
    String expectedUrl = "jdbc:postgresql://localhost:5432/" + testDb;

    try (MockedStatic<DriverManager> mockedDriver = mockStatic(DriverManager.class)) {
        mockedDriver.when(() -> DriverManager.getConnection(expectedUrl, testUser, testPass))
                .thenReturn(mockConnection);

        PostgresConnection postgresConn = new PostgresConnection(testDb, testUser, testPass);
        Connection resultConn = postgresConn.connectionToDatabase();

        assertNotNull(resultConn);
        mockedDriver.verify(() -> DriverManager.getConnection(expectedUrl, testUser, testPass), times(1));
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 15:57:10