Testcontainersを使用し、spring-bootのテスト開始時にoracle xeのdockerコンテナを起動してテスト終了時に停止する。
環境
- Windows 10 Pro x64
- docker desktop 3.1.0
事前準備
oracleのdocker
まずdockerでoracleを起動可能にする。
oracle xeのpre-builtのdocker準備
oracleの起動は時間かかるので少しでも早くするために行う。もし手動でなく自動テストで起動時間が多少かかっても問題無いならしなくても良い。
ソースコード
spring-bootであればSpring Initializrで作成するのが楽。以下ツイートで教えて頂きました。
CCされたw
— Toshiaki Maki 💉💉 (@making) 2021年1月19日
SpringでもQuarkusでもMicronautでもテストに使われているのでintegration testのデファクトになっているのではないでしょうか。https://t.co/1QzYeX77rVhttps://t.co/gF7jcD0anghttps://t.co/MrZgt9QQgf
Spring Initializrでも追加することができます。 pic.twitter.com/9DDxzMF0oP
plugins { id 'org.springframework.boot' version '2.4.3' id 'io.spring.dependency-management' version '1.0.11.RELEASE' id 'java' } group = 'com.example' version = '0.0.1-SNAPSHOT' sourceCompatibility = '15' configurations { compileOnly { extendsFrom annotationProcessor } } repositories { mavenCentral() } ext { set('testcontainersVersion', "1.15.1") } dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' compileOnly 'org.projectlombok:lombok' developmentOnly 'org.springframework.boot:spring-boot-devtools' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.testcontainers:junit-jupiter' testImplementation 'org.testcontainers:oracle-xe' } dependencyManagement { imports { mavenBom "org.testcontainers:testcontainers-bom:${testcontainersVersion}" } } test { useJUnitPlatform() }
今回はoracle-xeを使うのでorg.testcontainers:oracle-xe
を追加している。
まず、動作確認用のrepositoryを作っておく。
src/main/resources/application.properties
はいつも通りにoracleを起動済みの前提で接続設定などを記述する。
spring.datasource.url=jdbc:oracle:thin:@//localhost:1521/XE spring.datasource.username=system spring.datasource.password=oracle
import javax.persistence.Entity; import javax.persistence.Id; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @Entity @NoArgsConstructor @AllArgsConstructor public class Item { @Id String id; }
import org.springframework.data.jpa.repository.JpaRepository; public interface ItemRepository extends JpaRepository<Item, String> { }
以下がメインのテストクラス。
import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; import org.springframework.test.context.jdbc.Sql; import org.testcontainers.containers.OracleContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; @Sql(scripts = "classpath:schema-test.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) @Testcontainers @DataJpaTest @AutoConfigureTestDatabase(replace = Replace.NONE) class DemoApplicationTest { @Container static OracleContainer oracle = new OracleContainer("oracle/db-prebuilt:18.4.0-xe") .withUsername("system") .withPassword("oracle"); @DynamicPropertySource static void registerProperties(DynamicPropertyRegistry registry) { System.out.println(oracle.getJdbcUrl()); System.out.println(oracle.getUsername()); System.out.println(oracle.getPassword()); registry.add("spring.datasource.url", () -> oracle.getJdbcUrl()); registry.add("spring.datasource.username", () -> oracle.getUsername()); registry.add("spring.datasource.password", () -> oracle.getPassword()); } @Autowired JdbcTemplate template; @Autowired ItemRepository repository; @Test void test() { System.out.println(template.queryForObject("select 1 from dual", Long.class)); System.out.println(repository.count()); } }
以下はsrc/test/resources/schema-test.sql
CREATE TABLE item (id VARCHAR2(10) NOT NULL);
@Testcontainers
により@Container
で定義するコンテナが自動的に有効になる。今回はoracle-xeなのでtestcontainersのOracleContainer
を使用する。javadoc見ると@Testcontainers is a JUnit Jupiter extension
とか書いてあるのでそれ以前のバージョンでは使えないと思われる。
OracleContainer
にはoracleのdockerイメージ名とusernameとpasswordを指定している。これらの値は今回pre-built作成時に事前指定する想定なので、その時に設定した値を指定する。
@DynamicPropertySource
を使用してテスト起動時に動的にspring-bootのプロパティを書き換える*1。docker起動後にOracleContainer
から各種値が取得できるのでそれをspring.datasource.*
に設定する。なお、この例ではusernameやpasswordは固定なので動的に指定しなくてもよい。ポートは上記のコードだとランダムになるのでこうしている。環境によってはポートを固定値にしても良いと思う。
@AutoConfigureTestDatabase(replace = Replace.NONE)
は組み込みDBの自動起動が不要なのでこうしている。
@Sql(scripts = "classpath:schema-test.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
はテスト起動時にテーブルなど作成SQLを実行する。自分の知ってる方法がこれなので、他にやり方あるかもしれない。
起動時の様子
テストコード起動すると下記ような感じに自動的にコンテナが起動&停止する。
docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES bf02cc55e92f oracle/db-prebuilt:18.4.0-xe "/bin/sh -c 'exec $O…" 10 seconds ago Up 8 seconds (health: starting) 0.0.0.0:49200->1521/tcp, 0.0.0.0:49199->5500/tcp, 0.0.0.0:49198->8080/tcp zealous_noether 555a00685b91 testcontainers/ryuk:0.3.0 "/app" 11 seconds ago Up 10 seconds 0.0.0.0:49197->8080/tcp testcontainers-ryuk-9bdd7420-07f0-43ad-848f-fe060aec3298