KotlinでMockを使ったテストをする時にhas not been initializedでハマった話し
fundbookの藤田です。
もともとサーバーサイドはPHPerなのですが、ここ最近Kotlinを書くことが多くなり、Mockを使ったテストでドハマりしたので、自分への戒めと私と同じくKotlin初学者のために記事にします。
KotlinであるAPIの実装をしており、そのAPIではGoogle APIを叩いて、その結果を整形して返す処理をしています。
Google APIから返ってくるレスポンスが、その時によって変動であるために、Mockを使おうとなりました。
コードの全体的な構造としては、こんな感じです。
Resourceクラスは、Controllerにあたる箇所です。
Serviceクラスは、ビジネスロジックを持った箇所です。
Repositoryクラスは、データを取ってくる箇所です。
Google APIはRepositoryクラスに実装をしていて、RepositoryクラスをMock化してテストコードを書きます。
発生した事象
Genie AIに聞いてみると、こんな感じでアノテーションをつけてあげて、Mockとして返してほしい内容を書いて、インジェクションしてあげれば、Mockとしてすぐに使えるよとのことだったので、早速試してみました!
@QuarkusTest
class XXXXXServiceTest {
@Mock
lateinit var xxxxRepository: XXXXRepository
...
fun `XXXXをテストするよ`() {
Mockito.`when`(xxxxRepository.getXXX(email)).thenReturn(["Mockとしてほしい戻り値"])
XXXXXService.xxxxRepository = xxxxRepository
}
}
※だいぶ省略して書いてます。
しかし、、、
kotlin.UninitializedPropertyAccessException: lateinit property xxxxxRepository has not been initialized Mock
「なんでだ。。。」
「初期化がうまくいってない!?」
「インジェクションがうまくてきていないのか?」
何度も見返して、Genie AIにも聞いてみるが、問題なさそう。。
typoか設定ミスでうまくいってないんだろうと考え、ひたすらコードと睨めっこするも分からず、時間だけが過ぎていく。。。
こんな設定があったのか
エラーになった内容でググった結果これでした。
Mockを使う時は、BeforeEachアノテーションで MockitoAnnotations.openMocks(this)
をしないとダメだとのこと
@Mcok
だけ書いとけば、@Inject
みたいに初期化処理をしてくれるだろうと思ってましたが、そんなことはありませんでした。。
ドキュメントに記載がある通り、BeforeとAfterアノテーションでの設定が必要でした。MockitoAnnotations.openMocks
に1つずつ@Mock
した変数を設定しているが、引数にthisと指定してあげるだけで、@Mock
がついた変数の初期化をしてくれました。
しばらく、PHPerエンジニアでアノテーションにあまり触れる機会がなかったですが、今回の事象で久々にJavaのアノテーションを使ってたことを思い出したのと、改めてドキュメントを読むのは大切だと思いました。
株式会社fundbookについてはこちらから
M&Aサービスについてはこちらから
Discussion