Home
img of docs

通过结合 Mockito、H2 数据库等工具,展示如何在不连接实际数据库的情况下进行模拟测试,处理数据库事务、连接池等问题,

chou403

/ UnitTest

/ c:

/ u:

/ 4 min read


介绍

对JDBC这一层进行单元测试,可以通过模拟数据库连接或使用嵌入式数据库来实现。以下是这两种方法的详细步骤:

方法一:使用 Mockito 模拟 JDBC

1. 添加 Mockito 依赖

在 Maven 项目中添加 Mockito 依赖:

   <dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>3.11.2</version>
    <scope>test</scope>
</dependency>

在 Gradle 项目中添加 Mockito 依赖:

   testImplementation 'org.mockito:mockito-core:3.11.2'
2. 编写使用 Mockito 的单元测试

假设我们有一个简单的 DAO 类,用于从数据库中获取用户信息。

UserDao.java:

   import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class UserDao {
    private Connection connection;

    public UserDao(Connection connection) {
        this.connection = connection;
    }

    public User getUserById(Long id) throws SQLException {
        String query = "SELECT id, name FROM users WHERE id = ?";
        try (PreparedStatement stmt = connection.prepareStatement(query)) {
            stmt.setLong(1, id);
            try (ResultSet rs = stmt.executeQuery()) {
                if (rs.next()) {
                    return new User(rs.getLong("id"), rs.getString("name"));
                }
            }
        }
        return null;
    }
}

User.java:

   public class User {
    private Long id;
    private String name;

    public User(Long id, String name) {
        this.id = id;
        this.name = name;
    }

    // Getters and setters
}

UserDaoTest.java:

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

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

public class UserDaoTest {

    @Mock
    private Connection connection;
    @Mock
    private PreparedStatement preparedStatement;
    @Mock
    private ResultSet resultSet;

    private UserDao userDao;

    @BeforeEach
    public void setUp() throws SQLException {
        MockitoAnnotations.openMocks(this);
        userDao = new UserDao(connection);

        when(connection.prepareStatement(anyString())).thenReturn(preparedStatement);
        when(preparedStatement.executeQuery()).thenReturn(resultSet);
    }

    @Test
    public void testGetUserById() throws SQLException {
        when(resultSet.next()).thenReturn(true);
        when(resultSet.getLong("id")).thenReturn(1L);
        when(resultSet.getString("name")).thenReturn("John Doe");

        User user = userDao.getUserById(1L);
        assertNotNull(user);
        assertEquals(1L, user.getId());
        assertEquals("John Doe", user.getName());

        verify(preparedStatement, times(1)).setLong(1, 1L);
        verify(preparedStatement, times(1)).executeQuery();
    }

    @Test
    public void testGetUserById_NotFound() throws SQLException {
        when(resultSet.next()).thenReturn(false);

        User user = userDao.getUserById(1L);
        assertNull(user);

        verify(preparedStatement, times(1)).setLong(1, 1L);
        verify(preparedStatement, times(1)).executeQuery();
    }
}

方法二:使用嵌入式数据库进行测试

嵌入式数据库允许在测试环境中创建临时数据库,无需依赖外部的数据库服务。H2数据库是一个常用的嵌入式数据库。

1. 添加 H2 依赖

在 Maven 项目中添加 H2 依赖:

   <dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>1.4.200</version>
    <scope>test</scope>
</dependency>

在 Gradle 项目中添加 H2 依赖:

   testImplementation 'com.h2database:h2:1.4.200'
2. 编写测试

在测试中使用 H2 数据库创建表并插入数据,然后进行实际的数据库操作测试。

UserDaoTest.java:

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

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

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

public class UserDaoTest {

    private Connection connection;
    private UserDao userDao;

    @BeforeEach
    public void setUp() throws SQLException {
        connection = DriverManager.getConnection("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1");
        connection.createStatement().execute("CREATE TABLE users (id BIGINT PRIMARY KEY, name VARCHAR(255))");
        connection.createStatement().execute("INSERT INTO users (id, name) VALUES (1, 'John Doe')");
        userDao = new UserDao(connection);
    }

    @AfterEach
    public void tearDown() throws SQLException {
        connection.createStatement().execute("DROP TABLE users");
        connection.close();
    }

    @Test
    public void testGetUserById() throws SQLException {
        User user = userDao.getUserById(1L);
        assertNotNull(user);
        assertEquals(1L, user.getId());
        assertEquals("John Doe", user.getName());
    }

    @Test
    public void testGetUserById_NotFound() throws SQLException {
        User user = userDao.getUserById(2L);
        assertNull(user);
    }
}

代码解释

  1. Mockito 方式: 通过模拟 Connection,PreparedStatementResultSet 对象,可以在没有真实数据库连接的情况下测试 UserDao 的行为。
  2. 嵌入式数据库方式: 使用 H2 数据库在内存中创建临时表并插入数据,进行实际的数据库操作测试。

这两种方式各有优缺点:

  • Mockito: 适合快速单元测试,独立于外部资源,但不能测试实际的数据库交互逻辑。
  • 嵌入式数据库: 能够测试实际的数据库交互,但需要更多的环境设置和清理工作。

这两种方法结合使用,可以更全面地覆盖JDBC层的单元测试需求。