DEV Community

kaede
kaede

Posted on • Edited on

Kotlin Springboot -- Part 16 usecase の単体テスト (UT) を作る。port 以降は mockk を使う。

実装と方針


実装コード

@Component class PersonsUsecase( private val personPort: PersonPort, ) { fun getAllPersons():Persons { return personPort.getAllPersons() } 
Enter fullscreen mode Exit fullscreen mode

テストをする対象のコードはこれ
At Component で外部から使えるようにして
同じく At Component で外部から使えるようになっている
Port とその先の Gateway の getAllPersons を呼んで
Persons ドメインで受け取って、そのまま Rest に返す。


方針

TDD をやりたい。だから単体テストを先に書く。

実装のように Gateway のインスタンスをそのまま呼び出して使うと
Usecase の単体テストなのに、Gateway も同時にテストすることになってしまう。

それは Clean Archtechture な TDD ではない。
Rest のテストが通って、Usecase のテストが通って、そのあとに Gateway を実装したい。

なので Gateway を呼び出すための Port を mockk を用いてモックにする。



単体テスト


実際にやること

Port の偽物の中身を読んだ時にできる想定物と
Usecase の中身を読んだ時に成果物
これらが同じか比較する。


単体テストコード全体

import io.mockk.* import org.amshove.kluent.shouldBeEqualTo import org.junit.jupiter.api.Test class PersonsUsecaseTest { @Test fun `全てのPersonを取得する`() { val persons = mockk<Persons>() val personPort = mockk<PersonPort>() val target = PersonsUsecase(personPort) every { personPort.getAllPersons() } returns persons val actual = target.getAllPersons() actual shouldBeEqualTo persons } } 
Enter fullscreen mode Exit fullscreen mode

関数はじめと返り値のモック

 @Test fun `全てのPersonを取得する`() { val persons = mockk<Persons>() 
Enter fullscreen mode Exit fullscreen mode

At Test でテストコードだということを明記。
mockk で Persons 型の偽物インスタンスを作成。これは単純。


Port のモックの中身を呼び出す

同じく mockk で PersonPort 型のインスタンスを作成する。
しかし、これは中身のメソッドを呼び出されることになる。

val personPort = mockk<PersonPort>() target.getAllPersons() 
Enter fullscreen mode Exit fullscreen mode

たとえばこのまま mockk の偽物の中身を実装と同じ用に呼び出すと

no answer found for: PersonPort(#2).getAllPersons() io.mockk.MockKException: no answer found for: PersonPort(#2).getAllPersons() 
Enter fullscreen mode Exit fullscreen mode

PersonPort は mockk で作られた偽物である。
なので中身が呼び出せない。MockKException が起きる


Port のモックの中身を呼び出した時に何が返ってくるか決める

every { personPort.getAllPersons() } returns persons 
Enter fullscreen mode Exit fullscreen mode

ここで every { mockPort.functionName() } returns expectedMockName を使う。

すると {} 内部で Port のモックの中身の関数が呼ばれた時に
returns の後ろに書いた期待されるモックを返すようになる。


usecase として本体のインスタンスを呼び出す

val target = PersonsUsecase(personPort) 
Enter fullscreen mode Exit fullscreen mode

mock の port を usecase のクラスに渡してインスタンス化する。

Usecase 自体をモックにするのは次回。


まとめ

Usecase の UT を書くには

@Test fun `シナリオ名`() { 
Enter fullscreen mode Exit fullscreen mode

と書き始めて

返り値の型の Domain のモックを用意。
内部で使われる Port のモックを用意。

every {} return で Port のモックの使用される関数が呼ばれた場合
Domain のモックが返されるように設定する

Port のモックを使って Usecase のインスタンスを作る

usecase のインスタンスの使用される関数が呼ばれた場合
Port のモックの中身が呼ばれた場合と同一結果になるか比較する。

以上。


次回

Usecase も本物に限りなく近いモックを用意してテストするようにする。

Top comments (0)