JUnit実行前にDockerを自動起動させる


## 目次
* [概要](#概要)
* [環境](#環境)
* [各種設定ファイル](#各種設定ファイル)
* [実行結果](#実行結果)
* [参考](#参考)

## 概要
JUnit実行前に、Dockerコンテナ立ち上げの自動化を行う設定について記載しております。
※今回はDBのお話ですが、その他のサービスも同様に自動起動が可能です。
ソースコードだけ見たい方は[こちら](https://github.com/omoikane245/junit-example)から。

## 環境
* Kotlin 1.6.21
* Docker 20.10.21
* Docker-Compose 2.13.0

## 各種設定ファイル
* docker-compose.yml
```yml
version: "3"

services:
  # DB
  mysql:
    container_name: test-db
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: test
      MYSQL_USER: test
      MYSQL_PASSWORD: test
      LANG: C.UTF-8
      LANGUAGE: ja_JP
      TZ: 'Asia/Tokyo'
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
    volumes:
      - ./config/mysql/my.cnf:/etc/mysql/conf.d/my.cnf
      - ./config/mysql/sql:/docker-entrypoint-initdb.d
    ports:
      - "3306:3306"
  flyway: # テーブル作成や初期データ投入等で必要
    container_name: test-flyway
    image: boxfuse/flyway:5.1.4
    depends_on:
      - mysql
    volumes:
      - ./config/flyway/conf:/flyway/conf
      - ./config/flyway/migration:/flyway/sql
    entrypoint: >
      /bin/sh -c "
      until (/flyway/flyway clean) do echo 'waiting ...' & sleep 1; done;
      /flyway/flyway migrate;
      exit $$?;
      "

volumes:
  database:
    driver: local
```
* build.gradle.kts
```gradle
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import java.lang.Thread.sleep

plugins {
	id("org.springframework.boot") version "2.7.7"
	id("io.spring.dependency-management") version "1.0.15.RELEASE"
	kotlin("jvm") version "1.6.21"
	kotlin("plugin.spring") version "1.6.21"
    // ※Docker-Compose-Plugin導入
    id("com.avast.gradle.docker-compose") version "0.14.0"
    // ※静的解析用のPlugin導入。Dockerの自動起動とは無関係。
    id("io.gitlab.arturbosch.detekt") version "1.17.0"
}

group = "com.example"
version = "0.0.1-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_17

configurations {
	compileOnly {
		extendsFrom(configurations.annotationProcessor.get())
	}
}

repositories {
	mavenCentral()
}

dependencies {
	implementation("org.springframework.boot:spring-boot-starter-web")
	implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
	implementation("org.jetbrains.kotlin:kotlin-reflect")
	implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
	implementation("org.mybatis.spring.boot:mybatis-spring-boot-starter:2.3.0")
    implementation("com.mysql:mysql-connector-j")
	testImplementation("org.springframework.boot:spring-boot-starter-test")
    testImplementation("io.mockk:mockk:1.13.3")
    testImplementation("org.mybatis.spring.boot:mybatis-spring-boot-starter-test:2.3.0")
}

// ※静的解析用の設定。Dockerの自動起動とは無関係。
detekt {
    toolVersion = "1.17.0"
    config = files("./detekt.yml")
    buildUponDefaultConfig = true
}

// ※ここがDockerの自動起動の設定。
dockerCompose {
    useComposeFiles = listOf("./docker-compose.yml")
    isRequiredBy(project.tasks.test)
}

tasks.withType<KotlinCompile> {
	kotlinOptions {
		freeCompilerArgs = listOf("-Xjsr305=strict")
		jvmTarget = "17"
	}
}

tasks.withType<Test> {
    useJUnitPlatform()

    doFirst {
        sleep(10000)
    }
}

```

## 実行結果
* テスト実行時のログ
```bash
Starting Gradle Daemon...
Gradle Daemon started in 2 s 700 ms

> Task :compileKotlin UP-TO-DATE
> Task :compileJava NO-SOURCE
> Task :processResources
> Task :classes
> Task :compileTestKotlin UP-TO-DATE
> Task :compileTestJava NO-SOURCE
> Task :processTestResources
> Task :testClasses
> Task :composeUp
option '--no-ansi' is DEPRECATED ! Please use '--ansi' instead.
option '--no-ansi' is DEPRECATED ! Please use '--ansi' instead.
Network 2a0ac62906fca194ce686cf83871c6a5_junit-example__default  Creating
Network 2a0ac62906fca194ce686cf83871c6a5_junit-example__default  Created
Container test-db  Creating
Container test-db  Created
Container test-flyway  Creating
Container test-flyway  Created
Container test-db  Starting
Container test-db  Started
Container test-flyway  Starting
Container test-flyway  Started
option '--no-ansi' is DEPRECATED ! Please use '--ansi' instead.
option '--no-ansi' is DEPRECATED ! Please use '--ansi' instead.
option '--no-ansi' is DEPRECATED ! Please use '--ansi' instead.
Will use localhost as host of mysql
Will use localhost as host of flyway-clean-migrate
Probing TCP socket on localhost:3306 of 'test-db'
Waiting for TCP socket on localhost:3306 of 'test-db' (TCP connection on localhost:3306 of 'test-db' was disconnected right after connected)
Will use localhost as host of mysql
Waiting for TCP socket on localhost:3306 of 'test-db' (TCP connection on localhost:3306 of 'test-db' was disconnected right after connected)
Will use localhost as host of mysql
Waiting for TCP socket on localhost:3306 of 'test-db' (TCP connection on localhost:3306 of 'test-db' was disconnected right after connected)
... 中略

Will use localhost as host of mysql
TCP socket on localhost:3306 of 'test-db' is ready
+-------------+----------------+----------------+
| Name        | Container Port | Mapping        |
+-------------+----------------+----------------+
| test-db     | 3306           | localhost:3306 |
+-------------+----------------+----------------+
> Task :test
15:59:05.816 [Test worker] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating CacheAwareContextLoaderDelegate from class [org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate]
15:59:06.148 [Test worker] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating BootstrapContext using constructor [public org.springframework.test.context.support.DefaultBootstrapContext(java.lang.Class,org.springframework.test.context.CacheAwareContextLoaderDelegate)]
15:59:06.576 [Test worker] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating TestContextBootstrapper for test class [com.example.junitexample.mapper.UserMapperTest] from class [org.mybatis.spring.boot.test.autoconfigure.MybatisTestContextBootstrapper]
15:59:06.608 [Test worker] INFO org.mybatis.spring.boot.test.autoconfigure.MybatisTestContextBootstrapper - Neither @ContextConfiguration nor @ContextHierarchy found for test class [com.example.junitexample.mapper.UserMapperTest], using SpringBootContextLoader
... 中略
[org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener, org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener, org.springframework.boot.test.autoconfigure.webservices.client.MockWebServiceServerTestExecutionListener, org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener, org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener, org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.event.ApplicationEventsTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener, org.springframework.test.context.event.EventPublishingTestExecutionListener]
2023-01-04 15:59:19.197  INFO 20316 --- [    Test worker] o.s.t.c.s.DefaultTestContextBootstrapper : Using TestExecutionListeners: [org.springframework.test.context.web.ServletTestExecutionListener@74ea1cd0, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@179e8859, org.springframework.test.context.event.ApplicationEventsTestExecutionListener@477dc93, org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener@781ecbac, org.springframework.test.context.support.DependencyInjectionTestExecutionListener@5cb654e3, org.springframework.test.context.support.DirtiesContextTestExecutionListener@3f89d36c, org.springframework.test.context.transaction.TransactionalTestExecutionListener@520034a, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener@bedebe9, org.springframework.test.context.event.EventPublishingTestExecutionListener@6592a73b, org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener@14f11a56, org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener@11ce61d1, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener@759862a2, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener@543b855, org.springframework.boot.test.autoconfigure.webservices.client.MockWebServiceServerTestExecutionListener@197c3101, org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener@7b5e1b16]
2023-01-04 15:59:19.237  INFO 20316 --- [    Test worker] o.s.t.c.s.DefaultTestContextBootstrapper : Neither @ContextConfiguration nor @ContextHierarchy found for test class [com.example.junitexample.service.UserServiceTest$Delete], using DelegatingSmartContextLoader
2023-01-04 15:59:19.237  INFO 20316 --- [    Test worker] o.s.t.c.support.AbstractContextLoader    : Could not detect default resource locations for test class [com.example.junitexample.service.UserServiceTest$Delete]: no resource found for suffixes {-context.xml}.
2023-01-04 15:59:19.237  INFO 20316 --- [    Test worker] t.c.s.AnnotationConfigContextLoaderUtils : Could not detect default configuration classes for test class [com.example.junitexample.service.UserServiceTest$Delete]: Delete does not declare any static, non-private, non-final, nested classes annotated with @Configuration.
2023-01-04 15:59:19.253  INFO 20316 --- [    Test worker] o.s.t.c.s.DefaultTestContextBootstrapper : Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener, org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener, org.springframework.boot.test.autoconfigure.webservices.client.MockWebServiceServerTestExecutionListener, org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener, org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener, org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.event.ApplicationEventsTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener, org.springframework.test.context.event.EventPublishingTestExecutionListener]
2023-01-04 15:59:19.253  INFO 20316 --- [    Test worker] o.s.t.c.s.DefaultTestContextBootstrapper : Using TestExecutionListeners: [org.springframework.test.context.web.ServletTestExecutionListener@5d28e108, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@4d2bcca8, org.springframework.test.context.event.ApplicationEventsTestExecutionListener@4587675c, org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener@31e3b882, org.springframework.test.context.support.DependencyInjectionTestExecutionListener@2b592f17, org.springframework.test.context.support.DirtiesContextTestExecutionListener@3884e858, org.springframework.test.context.transaction.TransactionalTestExecutionListener@46c7c593, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener@6a4c4261, org.springframework.test.context.event.EventPublishingTestExecutionListener@88fe9a8, org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener@17d606c9, org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener@6f45a2fd, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener@6792aa3e, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener@1027cdcd, org.springframework.boot.test.autoconfigure.webservices.client.MockWebServiceServerTestExecutionListener@37e0614e, org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener@75707f05]
> Task :composeDown
option '--no-ansi' is DEPRECATED ! Please use '--ansi' instead.
option '--no-ansi' is DEPRECATED ! Please use '--ansi' instead.
Container test-flyway  Stopping
Container test-flyway  Stopped
Container test-db  Stopping
Container test-db  Stopped
option '--no-ansi' is DEPRECATED ! Please use '--ansi' instead.
Container test-flyway  Stopping
Container test-flyway  Stopping
Container test-flyway  Stopped
Container test-flyway  Removing
Container test-flyway  Removed
Container test-db  Stopping
Container test-db  Stopping
Container test-db  Stopped
Container test-db  Removing
Container test-db  Removed
Network 2a0ac62906fca194ce686cf83871c6a5_junit-example__default  Removing
Network 2a0ac62906fca194ce686cf83871c6a5_junit-example__default  Removed

BUILD SUCCESSFUL in 1m 40s
7 actionable tasks: 5 executed, 2 up-to-date
15:59:24:  'test' の実行を完了しました。

```

* テスト実行時のDocker-Desktopのステータス
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/534209/3366862d-f4fd-2bb3-bf6b-d7765475fb9d.png)

## 参考
[mysql-docker](https://hub.docker.com/_/mysql)
[flyway-docker](https://github.com/flyway/flyway-docker)
[gradle-docker-compose-plugin](https://github.com/avast/gradle-docker-compose-plugin)