アプリをテストしたくなった
wildflyでダウンロードアプリでも作ろうってことで、いろいろ試行錯誤しながら作成中。
状態遷移をテストする必要がでたから、いい方法ないか探したらDBRiderが活躍してるとのこと。何一つ知らないから調べながらメモ程度に更新。
環境
- MyBatis - 3.5.15
- JUnit5 - 5.10.2
- Database Rider - 1.41.1
※pom.xmlを一部省いて記載してます。
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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<properties>
<version.org.mybatis>3.5.15</version.org.mybatis>
<version.org.junit>5.10.2</version.org.junit>
<version.com.github.database-rider>1.41.1</version.com.github.database-rider>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>${version.org.junit}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${version.org.mybatis}</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
</dependency>
<dependency>
<groupId>com.github.database-rider</groupId>
<artifactId>rider-junit5</artifactId>
<version>${version.com.github.database-rider}</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>com.github.database-rider</groupId>
<artifactId>rider-core</artifactId>
</exclusion>
</exclusions>
<classifier>jakarta</classifier>
</dependency>
<dependency>
<groupId>com.github.database-rider</groupId>
<artifactId>rider-core</artifactId>
<version>${version.com.github.database-rider}</version>
<scope>test</scope>
<classifier>jakarta</classifier>
</dependency>
</dependencies>
</project>
設定ファイル
mybatis-config.xml
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="MANAGED"/>
<dataSource type="POOLED">
<property name="driver" value="org.postgresql.Driver" />
<property name="url"
value="jdbc:postgresql://localhost:5432/postgres" />
<property name="username" value="postgres" />
<property name="password" value="postgres" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper class="com.example.database.UserRepository"/>
</mappers>
</configuration>
dbunit.yml
dbunit.yml
caseInsensitiveStrategy: LOWERCASE
properties:
schema: public
connectionConfig:
driver: "org.postgresql.Driver"
url: "jdbc:postgresql://localhost:5432/postgres"
user: "postgres"
password: "postgres"
テーブル
table.yml
com/example/database/UserRepository/table.yml
users:
- id: 1
name: "alice"
age: 10
- id: 2
name: "bob"
age: 10
testUpdateBobsAge.yml
com/example/database/UserRepository/expected/testUpdateBobsAge.yml
users:
- id: 1
name: "alice"
age: 10
- id: 2
name: "bob"
age: 11
testInsertCarol.yml
com/example/database/UserRepository/expected/testInsertCarol.yml
users:
- id: 1
name: "alice"
age: 10
- id: 2
name: "bob"
age: 10
- id: 3
name: "Carol"
age: 14
testDeleteBob.yml
com/example/database/UserRepository/expected/testDeleteBob.yml
users:
- id: 1
name: "alice"
age: 10
コード
User.java
com/example/entity/User.java
package com.example.entity;
public class User {
private Long id;
private String name;
private Integer age;
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return this.age;
}
public void setAge(Integer age) {
this.age = age;
}
}
UserRepository.java
com/example/database/UserRepository.java
package com.example.database;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import com.example.entity.User;
public interface UserRepository {
public List<User> getAll();
public boolean updateAgeById(@Param("id") long id, @Param("age") long age);
public boolean insert(@Param("entity") User entity);
public boolean deleteById(@Param("id") long id);
}
UserRepositoryTest.java
com/example/repository/UserRepositoryTest.java
package com.example.repository;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import com.example.entity.User;
import com.github.database.rider.core.api.dataset.DataSet;
import com.github.database.rider.core.api.dataset.ExpectedDataSet;
import com.github.database.rider.junit5.DBUnitExtension;
@ExtendWith(DBUnitExtension.class)
class UserRepositoryTest {
static SqlSessionFactory sqlSessionFactory;
static Configuration configuration;
@BeforeAll
static void beforeAll() throws IOException {
try (InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml")) {
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
}
SqlSession sqlSession;
UserRepository userRepository;
@BeforeEach
void beforeEach() {
this.sqlSession = sqlSessionFactory.openSession();
this.userRepository = this.sqlSession.getMapper(UserRepository.class);
}
@AfterEach
void afterEach() {
this.sqlSession.close();
}
@Test
@DataSet("com/example/database/UserRepository/table.yml")
void testGetAll() {
List<User> users = this.userRepository.getAll();
assertEquals(1L, users.get(0).getId());
assertEquals(2L, users.get(1).getId());
}
@Test
@DataSet("com/example/database/UserRepository/table.yml")
@ExpectedDataSet("com/example/database/UserRepository/expected/testUpdateBobsAge.yml")
void testUpdateBobsAge() {
this.userRepository.updateAgeById(2L, 11L);
}
@Test
@DataSet("com/example/database/UserRepository/table.yml")
@ExpectedDataSet("com/example/database/UserRepository/expected/testInsertCarol.yml")
void testInsert() {
User entity = new User();
entity.setId(3L);
entity.setName("Carol");
entity.setAge(14);
this.userRepository.insert(entity);
}
@Test
@DataSet("com/example/database/UserRepository/table.yml")
@ExpectedDataSet("com/example/database/UserRepository/expected/testDeleteBob.yml")
void testDeleteBob() {
this.userRepository.deleteById(2L);
}
}
メモ
- dbunit.yml の connectionConfig 項目を省きたい
- MyBatis 設定ファイルから DBUnit に設定する方法ないか?
- @BeforeEachと@AfterEachのopen-close処理とSqlSession#getMapperを省きたい
MyBatisCDIExtension
cdi大好きだから、そういう風に振る舞う拡張機能書いた。
@InjectMapperを付与されてるフィールドを対象に自動的にSqlSession#getMapperからインスタンス取得を設定してる。
なんかのテストクラス.java
@InjectMapper
XXXDao xxxDao;
org/mybatis/cdi/junit/jupiter/InjectMapper.java
package org.mybatis.cdi.junit.jupiter;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface InjectMapper {
}
org/apache/ibatis/junit/jupiter/MyBatisExtension.java
package org.mybatis.cdi.junit.jupiter;
import java.lang.reflect.Field;
import java.util.Objects;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.util.ConfigUtil;
import org.junit.jupiter.api.extension.AfterAllCallback;
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.platform.commons.support.AnnotationSupport;
public class MyBatisCDIExtension
implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback {
private final ConfigUtil configUtil = ConfigUtil.load("mybatis-config.xml");
private SqlSessionFactory sqlSessionFactory;
private SqlSession sqlSession;
@Override
public void beforeAll(ExtensionContext context) throws Exception {
if (Objects.isNull(this.sqlSessionFactory)) {
this.sqlSessionFactory = this.configUtil.getDefault();
}
this.sqlSession = this.sqlSessionFactory.openSession();
}
@Override
public void beforeEach(ExtensionContext context) throws Exception {
Class<?> targetClass = context.getRequiredTestClass();
for (Field field : AnnotationSupport.findAnnotatedFields(targetClass, InjectMapper.class)) {
field.trySetAccessible();
field.set(context.getRequiredTestInstance(),
this.sqlSession.getMapper(field.getType()));
}
}
@Override
public void afterAll(ExtensionContext context) throws Exception {
if (Objects.nonNull(this.sqlSession)) {
this.sqlSession.close();
}
}
}