在写DAO层的单元测试时,我们往往会遇到一个问题,测试用例所依赖的数据库数据被修改或删除了,或者在一个新的环境下所依赖的数据库不存在,导致单元测试无法通过,进而构建失败。
在这种情况下,使用H2内存数据库来模拟数据库环境是一个很好的解决方案
H2是一个短小精干的嵌入式数据库引擎,主要的特性包括:
1: 免费、开源、快速2: 嵌入式的数据库服务器,支持集群
3: 提供JDBC、ODBC访问接口,提供基于浏览器的控制台管理程序
4: Java编写,可使用GCJ和IKVM.NET编译
5: 短小精干的软件,1M左右。
官方网站为:http://www.h2database.com/html/main.html
本文章来演示一个Spring Boot + Mybatis + H2的单元测试例子
先新建一个maven项目 spring-boot-test-h2
项目结构如下:
其中,Mapper是操作数据库的,也就是DAO层。
具体代码如下:
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.pp</groupId>
<artifactId>spring-boot-test-h2</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<name>spring-boot-test-h2</name>
<url>http://maven.apache.org</url>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.3.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.2.0</version>
</dependency>
<!-- 测试相关的依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
package com.pp.test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* 启动入口
*/
@SpringBootApplication
public class App {
public static void main( String[] args ) {
SpringApplication.run(App.class, args);
}
}
package com.pp.test.config;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Configuration;
/**
* 配置Mapper的扫描包路径
*/
@Configuration
@MapperScan("com.pp.test.mapper")
public class MybatisScanConfiguration {
}
package com.pp.test.entity;
public class User {
private Integer id;
private String username;
private String password;
private Integer status;
public User(){}
public User(String username, String password, Integer status) {
this.username = username;
this.password = password;
this.status = status;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
@Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", password=" + password + ", status=" + status + "]";
}
}
package com.pp.test.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import com.pp.test.entity.User;
public interface UserMapper {
@Options(useGeneratedKeys = true, keyProperty = "id") //回写自增的主键ID
@Insert("insert into users (username,password,status)values(#{username},#{password},#{status})")
public Integer addUser(User user);
@Delete("delete from users where id=#{0}")
public Integer deleteUserById(Integer id);
@Update("update users set username=#{username},password=#{password},status=#{status} where id=#{id}")
public Integer updateUser(User user);
@Select("select * from users where id=#{0}")
public User getById(Integer id);
@Select("select * from users")
public List<User> queryUserList();
}
package com.pp.test.mapper;
import java.util.List;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import com.pp.test.config.MybatisScanConfiguration;
import com.pp.test.entity.User;
@RunWith(SpringRunner.class)
/**
* 这里指定的classes是可选的。如果不指定classes,则spring boot会启动整个spring容器,很慢(比如说会执行一些初始化,ApplicationRunner、CommandLineRunner等等)。不推荐
* 指定的话,就只会初始化指定的bean,速度快,推荐
*/
@SpringBootTest(classes={DataSourceAutoConfiguration.class, MybatisAutoConfiguration.class, MybatisScanConfiguration.class})
public class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
public void testAddUser() {
User user = new User("admin","123",1);
userMapper.addUser(user);
Assert.assertNotNull(user.getId());
}
@Test
public void testDeleteUserById() {
User user = new User("admin","123",1);
userMapper.addUser(user);
Integer count = userMapper.deleteUserById(user.getId());
Assert.assertEquals(new Integer(1), count);
}
@Test
public void testUpdateUser() {
User user = new User("admin","123",1);
userMapper.addUser(user);
user.setUsername("hr");
userMapper.updateUser(user);
Assert.assertEquals("hr", user.getUsername());
}
@Test
public void testGetById() {
User user = new User("admin","123",1);
userMapper.addUser(user);
User obj = userMapper.getById(user.getId());
Assert.assertEquals("admin", obj.getUsername());
Assert.assertEquals("123", obj.getPassword());
Assert.assertEquals(new Integer(1), obj.getStatus());
}
@Test
public void testQueryUserList() {
User user = new User("admin","123",1);
userMapper.addUser(user);
List<User> users = userMapper.queryUserList();
Assert.assertTrue(!users.isEmpty());
}
}
init_table.sql
create table if not exists users (id int not null primary key auto_increment,username varchar(100),password varchar(100),status int);
application.properties(src/test/resources)
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:db_users;MODE=MYSQL;INIT=RUNSCRIPT FROM './src/test/resources/init_table.sql'
spring.datasource.username=
spring.datasource.password=
注意
1:这里面的INIT=RUNSCRIPT,这里用来初始化init_table.sql脚本的
2:maven在跑单元测试的时候,优先读取src/test/resources的配置文件,没有则读取src/main/resources下的配置文件。所以,这里的h2的数据库驱动配置务必配置在
src/test/resources/application.properties文件中。真正的数据库配置要配置在src/main/resources/application.properties文件中
最后,执行 mvn clean test 就可以跑单元测试了。