1. 概述

本教程将创建一个使用内存数据库进行测试的Spring应用。

在标准环境下,应用会配置独立的MySQL数据库,这需要安装运行MySQL服务器,并预先创建好用户和数据库。

为了简化测试流程,我们将跳过MySQL的额外配置,改用H2内存数据库运行JUnit测试。

2. Maven依赖

开发时需要以下依赖:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>6.0.6</version>
</dependency>
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-jpa</artifactId>
    <version>3.1.0</version>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>2.1.214</version>
</dependency>
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>6.4.2.Final</version>
</dependency>

最新版依赖可从Maven中央仓库下载:

3. 数据模型与仓库

创建一个简单的Student实体类:

@Entity
public class Student {

    @Id
    private long id;
    
    private String name;
    
    // 标准构造器、getter/setter
}

接着创建基于Spring Data JPA的仓库接口:

public interface StudentRepository extends JpaRepository<Student, Long> {
}

这样Spring就能自动生成Student对象的操作支持。

4. 分离属性源

为区分标准模式和测试模式的数据库配置,我们可以根据应用运行模式从不同位置读取数据库属性文件。

标准模式下属性文件位于src/main/resources,测试模式下则使用src/test/resources的同名文件

测试运行时,应用会优先查找src/test/resources下的文件。如果未找到,则使用src/main/resources下的文件。如果测试路径下存在同名文件,则会覆盖主路径下的配置。

4.1. 定义属性文件

src/main/resources创建persistence-student.properties,配置MySQL数据源:

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/myDb
jdbc.user=tutorialuser
jdbc.pass=tutorialpass

hibernate.dialect=org.hibernate.dialect.MySQLDialect
hibernate.hbm2ddl.auto=create-drop

使用上述配置需要预先创建myDb数据库和tutorialuser/tutorialpass用户。

为使用内存数据库测试,在src/test/resources创建同名文件,配置H2数据库:

jdbc.driverClassName=org.h2.Driver
jdbc.url=jdbc:h2:mem:myDb;DB_CLOSE_DELAY=-1;NON_KEYWORDS=KEY,VALUE

hibernate.dialect=org.hibernate.dialect.H2Dialect
hibernate.hbm2ddl.auto=create

我们配置了H2数据库运行在内存中,自动创建表结构,并在JVM退出时关闭并销毁数据库。

4.2. JPA配置

创建*@Configuration类,加载persistence-student.properties*属性文件,并创建数据源:

@Configuration
@EnableJpaRepositories(basePackages = "com.baeldung.persistence.dao")
@PropertySource("persistence-student.properties")
@EnableTransactionManagement
public class StudentJpaConfig {

    @Autowired
    private Environment env;
    
    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(env.getProperty("jdbc.driverClassName"));
        dataSource.setUrl(env.getProperty("jdbc.url"));
        dataSource.setUsername(env.getProperty("jdbc.user"));
        dataSource.setPassword(env.getProperty("jdbc.pass"));

        return dataSource;
    }
    
    // 配置entityManagerFactory
    
    // 配置transactionManager

    // 配置额外Hibernate属性
}

5. 创建JUnit测试

基于上述配置编写JUnit测试,使用StudentRepository保存和检索Student实体:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  classes = { StudentJpaConfig.class }, 
  loader = AnnotationConfigContextLoader.class)
@Transactional
public class InMemoryDBTest {
    
    @Resource
    private StudentRepository studentRepository;
    
    @Test
    public void givenStudent_whenSave_thenGetOk() {
        Student student = new Student(1, "john");
        studentRepository.save(student);
        
        Student student2 = studentRepository.findOne(1);
        assertEquals("john", student2.getName());
    }
}

测试将完全自包含运行——创建内存H2数据库,执行操作,最后关闭连接并销毁数据库,日志如下:

INFO: HHH000400: Using dialect: org.hibernate.dialect.H2Dialect
Hibernate: drop table Student if exists
Hibernate: create table Student (id bigint not null, name varchar(255), primary key (id))
Mar 24, 2017 12:41:51 PM org.hibernate.tool.schema.internal.SchemaCreatorImpl applyImportSources
INFO: HHH000476: Executing import script 'org.hibernate.tool.schema.internal.exec.ScriptSourceInputNonExistentImpl@1b8f9e2'
Hibernate: select student0_.id as id1_0_0_, student0_.name as name2_0_0_ from Student student0_ where student0_.id=?
Hibernate: drop table Student if exists

6. 总结

本示例展示了如何使用内存数据库运行自包含测试。

完整源码可在GitHub获取。

关键优势

  • 无需外部数据库依赖
  • 测试执行快速
  • 自动清理测试数据

⚠️ 注意

  • 确保测试路径属性文件覆盖主路径配置
  • 生产环境仍需使用真实数据库

原始标题:Self-Contained Testing Using an In-Memory Database | Baeldung