Spring Boot + JUnit5 + Kotlin でテストを書く

### 更新情報
本記事はSpring Boot 2.1がリリースされる前に記述したものになります。2.1からは[各種テストアノテーションに```@ExtendWith(SpringExtension.class)```を付与する変更](https://github.com/spring-projects/spring-boot/issues/13739)が入ったため、```@SpringBootTest```アノテーションを使用している場合は```@ExtendWith(SpringExtension::class)```を付与する必要はなくなりました。

# はじめに
本投稿ではSpring Bootを用いたプロジェクトにおいて、
どのようにJUnit5を導入するかご説明します。
JUnit5の詳しい記述方法に関して本投稿では触れませんのでご注意ください。
また本投稿では以下の構成を想定しています。
- Spring Boot: 2系
- JUnit5
- Kotlin: 1.2系
- Gradle: 4.6以上

# 準備
まずはSpring Bootプロジェクトを作成します。
Kotlinでプロジェクトを作成するとbuild.gradleは以下のような記述になるかと思います。
<details><summary>プロジェクト作成時に生成されるbuild.gradle</summary><div>

```gradle:build.gradle
buildscript {
    ext {
        kotlinVersion = '1.2.41'
        springBootVersion = '2.0.3.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
        classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}")
        classpath("org.jetbrains.kotlin:kotlin-allopen:${kotlinVersion}")
    }
}

apply plugin: 'kotlin'
apply plugin: 'kotlin-spring'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
compileKotlin {
    kotlinOptions {
        freeCompilerArgs = ["-Xjsr305=strict"]
        jvmTarget = "1.8"
    }
}
compileTestKotlin {
    kotlinOptions {
        freeCompilerArgs = ["-Xjsr305=strict"]
        jvmTarget = "1.8"
    }
}

repositories {
    mavenCentral()
}


dependencies {
    compile('org.springframework.boot:spring-boot-starter')
    compile("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
    compile("org.jetbrains.kotlin:kotlin-reflect")
    testCompile('org.springframework.boot:spring-boot-starter-test')
}
```
</div></details>

つぎにSpring BootでJUnit5を使用するための設定を記述していきます。
なおGradleはバージョン4.6でJUnit5がサポートされたため、それ以降のバージョンを使用してください。
dependencies以降の記述を以下のように変更します。

```gradle
dependencies {
    compile('org.springframework.boot:spring-boot-starter')
    compile("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
    compile("org.jetbrains.kotlin:kotlin-reflect")
    testCompile('org.springframework.boot:spring-boot-starter-test') {
       exclude module: 'junit' // 1
    }
    testImplementation('org.junit.jupiter:junit-jupiter-api') // 2
    testRuntimeOnly('org.junit.jupiter:junit-jupiter-engine') // 2
}

test {
    useJUnitPlatform() // 3
}
```

1. spring-boot-starter-testには4系がデフォルトで含まれるため、それを除外します。
2. junit-jupiter-apiとjunit-jupiter-engineの追加します。
3. こちらの記述でJUnit5を有効化します。

以上で準備は完了です。

# テストコードの作成
例えばJUnit4で書かれた以下のようなテストファイルがあるとします。

```kotlin
import org.junit.Assert
import org.junit.Before
import org.junit.Test
import org.junit.jupiter.api.assertThrows
import org.junit.runner.RunWith
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.junit4.SpringRunner

class Color {
    fun getRed(): String = "FF0000"
    fun throwException() { throw Exception() }
}

@RunWith(SpringRunner::class)
@SpringBootTest
class ColorTests {

    @Before
    fun setup() {
        // do something
    }

    @Test
    fun getRedTest() {
        Assert.assertEquals("FF0000", Color().getRed())
    }

    @Test
    fun throwExceptionTest() {
        assertThrows<Exception> {
            Color().throwException()
        }
    }
}
```

こちらをJUnit5で記述すると以下のようになります。

```kotlin
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.junit.jupiter.SpringExtension

class Color {
    fun getRed(): String = "FF0000"
    fun throwException() { throw Exception() }
}

@ExtendWith(SpringExtension::class) // 1
@SpringBootTest
class ColorTests {

    @BeforeEach // 2
    fun setup() {
        // do something
    }

    @Test
    fun getRedTest() {
        assertEquals("FF0000", Color().getRed())
    }

    @Test
    fun throwExceptionTest() {
        assertThrows<Exception> {
            Color().throwException()
        }
    }
}
```

1. JUnit5ではRunnerがExtensionになったため、それに伴う変更です。
2. JUnit5からいくつかのアノテーションが変更されました。

# まとめ
Spring BootでJUnit5を導入する方法を簡単にご紹介しました。

おさらいすると覚えておくべき点は4つです。
1. Gradle4.6以上
2. spring-boot-starter-testから4系を抜く
3. Junit5の依存関係を追加し、useJUnitPlatform()で有効化する
4. テストクラスに付与するアノテーションは@SpringBootTestと以下

```kotlin
@RunWith(SpringRunner::class) // これではなく、
@ExtendWith(SpringExtension::class) // これを書く
```

なにか間違いなどございましたらご指摘いただけると幸いです。