Kotlin Springboot -- Part 18 Gateway の UT と実装を作る
Published: 2022-11-27 06:00:10


## why

前回 Usecase の UT まで DI を活用して実装したので
今回は Gateway の UT をやる。

---


## Gateway のクラスから Test を空で作成

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xggul9pfb35rwwpk2uyc.png)

IntelliJ でクラス名にカーソルを当てて Alt Enter すると
テストの作成のウィンドウが開く。

メンバー込みでの生成を選ぶ。

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k3o47w52wzkg6hysfs78.png)

すると次にモジュールをどれにするかと、
内部のテストディレクトリのどこに作るかがでてくる。

Gateway のディレクトリの test/kotlin を選ぶ。


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9epvkz4k5o8mmykrrxer.png)

するとこのように
gateway モジュールの src/main/ と並列の src/test/ の
内部にクラス名+Test の名前でテストファイルが生成される。

```kotlin
package com.kaede

import org.junit.jupiter.api.Test

import org.junit.jupiter.api.Assertions.*

internal class PersonGatewayTest {

    @Test
    fun getAllPersons() {
    }
}
```

ここまで中身が生成される。


---

## Gateway の依存を記入する

Gateway の依存モジュールを
build.gradle.kts に記入して maven reload する

```kotlin
    implementation(project(":port"))
    implementation(project(":driver"))
    implementation(project(":domain"))
```

* Gateway が実装する元のインターフェイスの Port
* 関数の中身で使う Driver
* Driver から返ってきた Entity を詰め替える為の Domain


----


## Driver モジュールに Entity と Driver のクラスたちを作成

DB から返ってくる型として Entity の入れ物が必要。

Driver モジュールと密結合なので、Driver モジュール内部に作る。

https://dev.to/kaede_io/kotlin-springboot-part-6-driver-de-entity-nodetakurasutoinsutansuwozuo-ri-gateway-de-domain-nibian-huan-suru-39ic

Entity についてはここを参照。
String や Int などの Primary な値を入れる。

```kotlin
  data class PersonEntity(val name: String , val age: Int)
```

PersonEntity の Entity を作って

```kotlin
class PersonDriver {
    fun findAll() : List<PersonEntity>{
        TODO()
    }
}
```

返り値が PersonEntity のリストになる PersonDriver のガワを作る。


---

## GatewayTest のためのライブラリの依存を追加する

build.gradle.kts に

```kotlin
    testImplementation("org.amshove.kluent:kluent:1.68")
    testImplementation("io.mockk:mockk:1.13.2")
```

Usecase と同じく、Gateway にも kluent と mockk を追加する

```kotlin
internal class PersonGatewayTest {
    @InjectMockKs
    lateinit var personGateway: PersonGateway
    @MockK
    lateinit var driver: PersonDriver
```

すると Gateway でも Mockk シリーズが使えるようになる。
Gateway と Driver を作っておく

```kotlin
    @Test
    fun `全ての Person を取得する`() {
        MockKAnnotations.init(this)
        val persons = mockk<Persons>()
        val personsEntity = listOf(mockk<PersonEntity>())

        every { driver.findAll() } returns personsEntity

        target.getAllPersons() shouldBeEqualTo personsEntity
    }
```

こうやって、
Usecase と同じようにテストを作ると正しくない。

なぜなら Usecase では

* Usecase の関数本体の返り値
* 内部で Port から呼ばれる Gateway の返り値

ともに Domain だったので比較ができたが

Gateway では

* Gateway の関数本体の返り値は Domain
* Driver の返り値は Entity

なので型で比較することができない。

```kotlin
    @Test
    fun `全ての Person を取得する`() {
        MockKAnnotations.init(this)

        val personsEntity = listOf(
            PersonEntity("ドメイン太郎", 100),
            PersonEntity("ドメイン次郎", 80),
        )
        every { driver.findAll() } returns personsEntity

        val persons = Persons(listOf(
            Person(
                Name("ドメイン太郎"), Age(100)
            ),
            Person(
                Name("ドメイン次郎"), Age(80)
            ),
        ))

        target.getAllPersons() shouldBeEqualTo persons
    }
```

なので、実際に Entity と Domain を同じ値で作って

Enitity と同じ値で作られた Domain ができるかをテストする。

---

## Gateway の実装

TDD として、テスト後に実装をする

```kotlin
@Component
class PersonGateway() : PersonPort { // Port の関数を実装するから
    override fun getAllPersons(): Persons {
        TODO()
        // return Persons(listOf(Person(Name("a"),Age(2))))
    }
}
```

現在のガワがこれ。
ここにテストと同じように Driver を Null で定義して

```kotlin
@Component
class PersonGateway() : PersonPort { // Port の関数を実装するから
    @Autowired
    lateinit var personDriver: PersonDriver
    override fun getAllPersons(): Persons {
        val persons = personDriver.findAll()
        val personsDomain= persons.map { personEntity ->
            Person(Name(personEntity.name), Age(personEntity.age))
        }
        return Persons(personsDomain)
    }
}
```

Driver の結果の Entity を Map して Person の list にして
Persons に詰めて返す

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ut7wokw0wuzev5vls6le.png)

これで Gateway も UT と実装を作成できた！

```php
Expected :Persons(list=[Person(name=Name(value=ドメイン太郎),
 age=Age(value=100)), Person(name=Name(value=ドメインさぶ郎),
 age=Age(value=80))])

Actual   :Persons(list=[Person(name=Name(value=ドメイン太郎), 
age=Age(value=100)), Person(name=Name(value=ドメイン次郎), 
age=Age(value=80))])
```

Expected の Domain の中身だけをさぶろうにするとちゃんと落ちる。