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)
解决方案
问题根源
- Mockito默认不支持静态方法Mock:
DriverManager.getConnection是静态方法,你当前的写法无法mock这个方法,导致实际调用了真实的JDBC连接逻辑。加上测试时ApplicationProperties未配置模拟值,生成的JDBC URL不完整,触发驱动找不到的错误。 - 依赖与测试逻辑耦合:待测试类直接依赖
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
相关产品推荐
相关产品推荐

