曹耘豪的博客

基于H2的Mybatis单元测试

  1. 背景
  2. 特性
  3. 代码示例,开箱可用
    1. 引入测试基类
    2. 单元测试类(业务测试类)
    3. 执行流程(每个Test)

背景

现在自测大部分情况都是启动spring,通过http请求进行本地测试,依赖本地或者开发环境的数据库,不太方便,并且存在数据污染的问题。并且,如果我们修改了SQL,还需要重新进行一边所有测试,非常麻烦,所以支持单个Mapper的不依赖外部数据库的单测迫在眉睫!

特性

代码示例,开箱可用

引入测试基类

SqlSession测试基类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
public class BaseMybatisSqlSessionTest {
private final List<String> xmlResourcePaths = new ArrayList<>();
private final List<String> initSqlResourcePaths = new ArrayList<>();
private final List<Class<? extends TypeHandler<?>>> typeHandlerClassList = new ArrayList<>();
protected SqlSession sqlSession;

@SneakyThrows
@BeforeEach
public void _baseInit() {
sqlSession = initSqlSession();
executeInitSql();
}

@AfterEach
public void _baseClear() {
sqlSession.close();
}

@SneakyThrows
private SqlSession initSqlSession() {
String url = "jdbc:h2:mem:test;MODE=MySQL";
DataSource dataSource = new SimpleDriverDataSource(Driver.load(), url);

Configuration configuration = new Configuration();
Environment environment = new Environment("test", new JdbcTransactionFactory(), dataSource);
configuration.setEnvironment(environment);
TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
for (Class<?> typeHandlerClass : typeHandlerClassList) {
typeHandlerRegistry.register(typeHandlerClass);
}
// 解析XML Mapper
for (String xmlResourcePath : xmlResourcePaths) {
ClassPathResource mapperResource = new ClassPathResource(xmlResourcePath);
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperResource.getInputStream(), configuration, mapperResource.toString(), configuration.getSqlFragments());
xmlMapperBuilder.parse();
}

SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(configuration);

return factory.openSession();
}

private void executeInitSql() {
for (String path : initSqlResourcePaths) {
executeSqlFile(path);
}
}

@SneakyThrows
protected void executeSqlFile(String path) {
ClassPathResource resource = new ClassPathResource(path);
String sql = IOUtils.toString(resource.getInputStream(), StandardCharsets.UTF_8);
executeSql(sql);
}

@SneakyThrows
protected void executeSql(String sql) {
try (PreparedStatement statement = sqlSession.getConnection().prepareStatement(sql)) {
statement.execute();
}
}

protected void addXmlResourcePath(String path) {
xmlResourcePaths.add(path);
}

protected void addInitSqlResourcePath(String path) {
initSqlResourcePaths.add(path);
}

protected void addTypeHandlerClass(Class<? extends TypeHandler<?>> clazz) {
typeHandlerClassList.add(clazz);
}

}

对于单个Mapper的简单测试,提供一个基类,继承该基类可直接使用mapper

单个Mapper测试基类,继承自SqlSession测试类
1
2
3
4
5
6
7
8
9
10
11
@RequiredArgsConstructor
public class BaseMybatisSingleMapperTest<Mapper> extends BaseMybatisSqlSessionTest {
private final Class<Mapper> mapperClass;
protected Mapper mapper;

@BeforeEach
public void baseMybatisSingleMapperTestInit() {
mapper = sqlSession.getMapper(mapperClass);
}

}

单元测试类(业务测试类)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class EntityMapperTest extends BaseMybatisSingleMapperTest<EntityMapper> {

public EntityMapperTest() {
super(EntityMapper.class);
addXmlResourcePath("/entity-mapper.xml");
addInitSqlResourcePath("/schema.sql");
}

@Test
public void test1() {
insert("1");

Entity result = mapper.findByName("1");

assertNotNull(result);
}

@Test
public void test2() {
insert("2");

Entity result = mapper.findByName("1");

assertNull(result);
}

private void insert(String name) {

Entity e = new Entity();
e.setName(name);

mapper.insert(e);
}

}

执行流程(每个Test)

   / 
  , ,