使用内存数据库,剔除单元测试的数据库环境依赖。
题图:from Zoommy
一. 背景
一般来说,在开发单元测试时,我们都是使用一套测试用数据库环境,或者直接使用开发机数据库环境进行测试。但是这种方式有个明显的弊端:前者会极大依赖网络状况、后者会因开发者环境不同而影响测试。
使用H2可以很大程度上解决上述问题。
what’s H2?H2是一个纯java编写的内存数据库,core只有1M左右,简小精悍。他支持模拟mysql,并且与spring配合良好,因此十分适合用来模拟单元测试中的数据库环境。不过也有不足之处,无法兼容所有mysql语法,毕竟是模拟,但还是可以满足大多数需求的。
二. 几种模拟数据库方式对比
三. 步骤
框架环境为:spring+junit+mybatis。
1. 依赖
建议使用1.3.176+的版本,因为1.3.176新添加了一些mysql语法的支持,使用起来会更加方便。
maven
12345678910<properties><h2.version>1.3.176</h2.version></properties><!--H2 单测--><dependency><groupId>com.h2database</groupId><artifactId>h2</artifactId><version>${h2.version}</version><scope>test</scope></dependency>gradle
1234// 方式一compile group: 'com.h2database', name: 'h2', version: '1.3.176'// 方式二compile 'com.h2database:h2:1.3.176'
2. 配置datasource和sql脚本
在test.resource包下,添加yourName.xml(在junit测试类上要用@ContextConfiguration注解引用该xml)。这个datasource会自动覆盖main.resource包下datasource的定义。
可以看到,配置中指定了三个sql脚本。
H2_TYPE.sql
是指定H2的模式,这里设置为兼容mysql模式1SET MODE MySQL;INIT_TABLE.sql
初始化表结构,这里给个简单示例1234CREATE TABLE `tt` (`id` int(10) unsigned NOT NULL AUTO_INCREMENT,PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;INIT_DATA.sql
插入数据到数据库,这里也是简单示例1INSERT INTO `tt` (id) VALUES (1);
四. FAQ
1. 配置成功后总是提示sql语法错误,但是sql在mysql下都能正常执行。
A:H2模拟mysql暂时无法支持所有mysql语法,两者有所区别,部分语法H2不支持,会导致报错。i.e.在H2的1.3.176
版本前,不支持create table
后面的Default Charset=utf8
。
2. 标识符报错:Syntax error in SQL statement "CREATE TABLE ""TT"" ( "; expected "identifier";
A:通过jdbc:script
标签配置的sql脚本默认使用分号作为分隔符,如果没有找到就会使用\n
做分隔符,因此这个报错很有可能是在sql末尾忘记了分号。还有一种可能就是表名或者列名使用了单引号'tableName'
,正确方式:tableName
或者tableName
也即要么不加,要么就是加```。
3. 索引报错:Index "status" already exists
A:H2的key或者unique key是数据库级别而非表级别,因此两张表内存在同名key会遇到这个报错,需要保证数据库内key的命名唯一。
4. 常见mysql兼容问题_
- 不支持表级别的Comment
- 插入语句单引号中的
\'
转义不支持 - H2 UNIQUE KEY是数据库级别的
- 无法执行多个Update语句
- 列别名无法用于子查询