why
前回 Usecase の UT まで DI を活用して実装したので
今回は Gateway の UT をやる。
Gateway のクラスから Test を空で作成
IntelliJ でクラス名にカーソルを当てて Alt Enter すると
テストの作成のウィンドウが開く。
メンバー込みでの生成を選ぶ。
すると次にモジュールをどれにするかと、
内部のテストディレクトリのどこに作るかがでてくる。
Gateway のディレクトリの test/kotlin を選ぶ。
するとこのように
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() {
}
}
ここまで中身が生成される。
Gateway の依存を記入する
Gateway の依存モジュールを
build.gradle.kts に記入して maven reload する
implementation(project(":port"))
implementation(project(":driver"))
implementation(project(":domain"))
- Gateway が実装する元のインターフェイスの Port
- 関数の中身で使う Driver
- Driver から返ってきた Entity を詰め替える為の Domain
Driver モジュールに Entity と Driver のクラスたちを作成
DB から返ってくる型として Entity の入れ物が必要。
Driver モジュールと密結合なので、Driver モジュール内部に作る。
Entity についてはここを参照。
String や Int などの Primary な値を入れる。
data class PersonEntity(val name: String , val age: Int)
PersonEntity の Entity を作って
class PersonDriver {
fun findAll() : List<PersonEntity>{
TODO()
}
}
返り値が PersonEntity のリストになる PersonDriver のガワを作る。
GatewayTest のためのライブラリの依存を追加する
build.gradle.kts に
testImplementation("org.amshove.kluent:kluent:1.68")
testImplementation("io.mockk:mockk:1.13.2")
Usecase と同じく、Gateway にも kluent と mockk を追加する
internal class PersonGatewayTest {
@InjectMockKs
lateinit var personGateway: PersonGateway
@MockK
lateinit var driver: PersonDriver
すると 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
}
こうやって、
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
}
なので、実際に Entity と Domain を同じ値で作って
Enitity と同じ値で作られた Domain ができるかをテストする。
Gateway の実装
TDD として、テスト後に実装をする
@Component
class PersonGateway() : PersonPort { // Port の関数を実装するから
override fun getAllPersons(): Persons {
TODO()
// return Persons(listOf(Person(Name("a"),Age(2))))
}
}
現在のガワがこれ。
ここにテストと同じように 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)
}
}
Driver の結果の Entity を Map して Person の list にして
Persons に詰めて返す
これで 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))])
Expected の Domain の中身だけをさぶろうにするとちゃんと落ちる。
Top comments (0)