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は動的に書き換えなくてもよいが、ここではコンテナから取った値で上書きしている。