DEV Community

kaede
kaede

Posted on

Kotlin Springboot -- Part 18 Gateway の UT と実装を作る

why

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


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

Image description

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

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

Image description

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

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

Image description

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

package com.kaede

import org.junit.jupiter.api.Test

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

internal class PersonGatewayTest {

    @Test
    fun getAllPersons() {
    }
}
Enter fullscreen mode Exit fullscreen mode

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


Gateway の依存を記入する

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

    implementation(project(":port"))
    implementation(project(":driver"))
    implementation(project(":domain"))
Enter fullscreen mode Exit fullscreen mode
  • 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 な値を入れる。

  data class PersonEntity(val name: String , val age: Int)
Enter fullscreen mode Exit fullscreen mode

PersonEntity の Entity を作って

class PersonDriver {
    fun findAll() : List<PersonEntity>{
        TODO()
    }
}
Enter fullscreen mode Exit fullscreen mode

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


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

build.gradle.kts に

    testImplementation("org.amshove.kluent:kluent:1.68")
    testImplementation("io.mockk:mockk:1.13.2")
Enter fullscreen mode Exit fullscreen mode

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

internal class PersonGatewayTest {
    @InjectMockKs
    lateinit var personGateway: PersonGateway
    @MockK
    lateinit var driver: PersonDriver
Enter fullscreen mode Exit fullscreen mode

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

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

        every { driver.findAll() } returns personsEntity

        target.getAllPersons() shouldBeEqualTo personsEntity
    }
Enter fullscreen mode Exit fullscreen mode

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

なぜなら Usecase では

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

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

Gateway では

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

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

    @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
    }
Enter fullscreen mode Exit fullscreen mode

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

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


Gateway の実装

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

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

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

@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)
    }
}
Enter fullscreen mode Exit fullscreen mode

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

Image description

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

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))])
Enter fullscreen mode Exit fullscreen mode

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

Top comments (0)