WSL2上のdockerでMySQLを起動してユニットテストを記述する。Testcontainersでテスト時にコンテナを自動的に開始・終了をする。
環境
Spring Initializr で「testcontainer」と入力すれば依存性の記述を自動生成してくれる。
plugins {
id 'org.springframework.boot' version '2.6.2'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'
repositories {
mavenCentral()
}
ext {
set('testcontainersVersion', "1.16.2")
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.testcontainers:junit-jupiter'
testImplementation "org.testcontainers:mysql"
runtimeOnly 'mysql:mysql-connector-java'
}
dependencyManagement {
imports {
mavenBom "org.testcontainers:testcontainers-bom:${testcontainersVersion}"
}
}
test {
useJUnitPlatform()
}
適当なエントリーポイントと適当なSQL実行を作る。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Appliction {
public static void main(String[] args) {
SpringApplication.run(Appliction.class, args);
}
}
@Component
public class SampleJdbc {
@Autowired
DataSource ds;
public void hoge() {
JdbcTemplate template = new JdbcTemplate(ds);
System.out.println(template.queryForObject("select count(*) from sample_table", Long.class));
}
}
プロジェクト直下のinitsql
ディレクトリにDB初期化SQLを配置する。
initsql/1_create-database.sql
create DATABASE if not exists sample_db;
USE sample_db;
initsql/2_create-table.sql
CREATE TABLE sample_table (id VARCHAR(20) NOT NULL DEFAULT '');
以下がテストコード。
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.testcontainers.containers.BindMode;
import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
@SpringBootTest
@Testcontainers
public class MysqlSampleTest {
@Container
static MySQLContainer mysql = new MySQLContainer<>("mysql")
.withUsername("root")
.withPassword("password")
.withDatabaseName("sample_db")
.withExposedPorts(3306)
.withFileSystemBind(
"/mnt/c/Users/ts-masaharu.kagami/eclipse-expr/testcontainerssample/lib/initsql",
"/docker-entrypoint-initdb.d",
BindMode.READ_WRITE);
@DynamicPropertySource
static void registerProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", () -> mysql.getJdbcUrl());
registry.add("spring.datasource.username", () -> mysql.getUsername());
registry.add("spring.datasource.password", () -> mysql.getPassword());
}
@Autowired
SampleJdbc template;
@Test
void test() {
template.hoge();
}
}
ホストの初期化SQLディレクトリとコンテナをバインドするためwithFileSystemBind
を使用する。docker-entrypoint-initdb.d
の詳細は省略するがコンテナ起動時にこのディレクトリ下のSQLを実行する。初期化SQLの実行手段は他にもあり、withClasspathResourceMapping
でクラスパス下のディレクトリをバインドしたり、withInitScript
で指定する手段や、springの@Sqlもある。
@DynamicPropertySource
では、コンテナ起動時にランダムで指定されるポート番号を取得してJDBC URLを上書きする。username, passwordは動的に書き換えなくてもよいが、ここではコンテナから取った値で上書きしている。