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)