主要针对MybatisPlus与H2内存数据库进行单元测试。
现如今我们在单元测试中针对service/DAO层测试时,存在以下重要问题:
1.需要启动完整的Spring容器,造成启动时间过长
2.依赖中间件过多,测试配置文件需要编写与DAO层测试无意义的配置
3.需要搭建各种环境(mysql,redis,kafka等)
4.每个测试用例都对应一套SQL,并且会对本地的数据库造成影响
所以针对上述问题,我们在单元测试要达到以下目的:
1.针对DAO层单元测试(service层会互相引用和循环依赖)
2.不启动完整容器,不需要多余配置
3.使用内存数据库,不需要搭建其他环境,保证测试隔离
4.单个测试用例对应自己的SQL
H2内存数据库的优势
1.占用空间小
2.即用即消
3.读写速度很快
4.支持嵌入模式使用数据库,在项目中只需导入依赖就可以使用
引入核心依赖
引入依赖,注意版本之间的兼容性
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.0</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter-test</artifactId>
<version>3.4.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.200</version>
<scope>test</scope>
</dependency>
方式一:配置文件
主要讲解测试用例的配置,对于DAO层接口不做详细介绍。@MybatisPlusTest注解默认情况下,它将配置MyBatis(MyBatis-Spring)组件(SqlSessionFactory
和SqlSessionTemplate
),配置MyBatis映射器接口以及配置内存嵌入式数据库。默认情况下,MyBatis测试是事务性的,并且在每个测试结束时都会回滚。
@MybatisPlusTest
@RunWith(SpringRunner.class)
@MapperScan("com.XXX.mapper") //扫描要测试的DAO层接口
@ActiveProfiles("test")
public class UserMapperTest {
......
}
配置文件application-XXXX.yml,指定初始化数据库的SQL文件
spring:
datasource:
schema: classpath:init_table.sql
方式二:注解
测试用例相关注解,@Sql会初始化指定的SQL文件
@MybatisPlusTest
@RunWith(SpringRunner.class)
@MapperScan("com.XXX.mapper")
@Sql("create.sql")
public class UserMapperTest {
. . .
}
@Sql注解细节说明
@Sql用于注释测试类或测试方法,以配置在集成测试期间针对给定数据库运行的SQL脚本
-
不加“/”,classpath下与测试类路径相同目录检测脚本
@Sql(“create.sql")
-
加“/”,classpath下检测脚本
@Sql(“/insert.sql")
-
声明多个@Sql集合
@Sql(“/create.sql") @Sql("/insert.sql") public class UserMapperTest { . . . }
默认脚本检测
如果未指定SQL脚本或语句,则尝试根据@Sql声明的位置来检测脚本。
- 类级别的声明:如果带注解的测试类为 com.example.Mytest,则相应的默认脚本为classpath:com/example/MyTest.sql
- 方法级别的声明:如果测试类com.example.Mytest中testMethod()方法上声明注解@Sql,则相应的默认脚本为classpath:com/example/MyTest.testMethod.sql
类似与@Sql注解,还有其他@SqlConfig,@SqlMergeModel注解,详细使用参考spring官网
注意事项
如果出现以下错误:
Caused by: java.lang.IllegalStateException: Failed to replace DataSource with an embedded database for tests. If you want an embedded database please put a supported one on the classpath or tune the replace attribute of @AutoConfigureTestDatabase.
解决
1.引入H2, HSQL or Derby数据库依赖,在启动时会加载H2, HSQL or Derby。
至于为什么会使用这3个数据库,先看@MybatisPlusTest注解,他上面标注了@AutoConfigureTestDatabase注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@BootstrapWith(MybatisPlusTestContextBootstrapper.class)
@ExtendWith({SpringExtension.class})
@OverrideAutoConfiguration(
enabled = false
)
@TypeExcludeFilters({MybatisPlusTypeExcludeFilter.class})
@Transactional
@AutoConfigureCache
@AutoConfigureMybatisPlus
@AutoConfigureTestDatabase
@ImportAutoConfiguration
public @interface MybatisPlusTest {
...
}
在进入@AutoConfigureTestDatabase,他这里使用了一个枚举类型EmbeddedDatabaseConnection
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@ImportAutoConfiguration
@PropertyMapping("spring.test.database")
public @interface AutoConfigureTestDatabase {
@PropertyMapping(
skip = SkipPropertyMapping.ON_DEFAULT_VALUE
)
AutoConfigureTestDatabase.Replace replace() default AutoConfigureTestDatabase.Replace.ANY;
EmbeddedDatabaseConnection connection() default EmbeddedDatabaseConnection.NONE;
public static enum Replace {
ANY,
AUTO_CONFIGURED,
NONE;
private Replace() {
}
}
}
进入EmbeddedDatabaseConnection,它里面定义了NONE、H2、DEREY、HSQL
public enum EmbeddedDatabaseConnection {
NONE((EmbeddedDatabaseType)null, (String)null, (String)null),
H2(EmbeddedDatabaseType.H2, DatabaseDriver.H2.getDriverClassName(), "jdbc:h2:mem:%s;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE"),
DERBY(EmbeddedDatabaseType.DERBY, DatabaseDriver.DERBY.getDriverClassName(), "jdbc:derby:memory:%s;create=true"),
HSQL(EmbeddedDatabaseType.HSQL, DatabaseDriver.HSQLDB.getDriverClassName(), "org.hsqldb.jdbcDriver", "jdbc:hsqldb:mem:%s");
至于在@AutoConfigureTestDatabase中 EmbeddedDatabaseConnection connection() default EmbeddedDatabaseConnection.NONE;它默认已经使用NONE,但是在启动时它还是会加载数据库,之后用debug跟踪一下,在EmbeddedDatabaseConnection枚举类中有这样的方法,如果你引入H2、DEREY、HSQL数据库,他会优先使用这三个,没有就返回NONE。
public static EmbeddedDatabaseConnection get(ClassLoader classLoader) {
EmbeddedDatabaseConnection[] var1 = values();
int var2 = var1.length;
for(int var3 = 0; var3 < var2; ++var3) {
EmbeddedDatabaseConnection candidate = var1[var3];
if (candidate != NONE && ClassUtils.isPresent(candidate.getDriverClassName(), classLoader)) {
return candidate;
}
}
return NONE;
}
2.使用本地数据库,添加@AutoConfigureTestDatabase(replace = Replace.NONE)。
Flyway注意事项
项目中有Flyway依赖时,@MybatisPlusTest会自动根据Flyway默认路径中加载sql,如果不使用Flyway,在@
MybatisPlusTest注解中排除FlywayAutoConfiguration.class配置类
@MybatisPlusTest(
excludeAutoConfiguration = FlywayAutoConfiguration.class
)
org.junit.Test与org.junit.jupiter.api.Test
在单元测试中使用org.junit.Test注解,如果使用org.junit.jupiter.api.Test注解,会出现以下警告:
H2不兼容的mysql关键字
- engine=InnoDB
- 表级comment
- default charset
- 表中创建普通索引和外键索引
其他
- 测试的mapper接口上要加@Mapper注解,或者在测试类上使用@MapperScan
- 使用@MybatisTest测试不了mybatis-plus封装的方法