🐡

KotlinでMockを使ったテストをする時にhas not been initializedでハマった話し

2024/02/29に公開

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か設定ミスでうまくいってないんだろうと考え、ひたすらコードと睨めっこするも分からず、時間だけが過ぎていく。。。

こんな設定があったのか

エラーになった内容でググった結果これでした。
https://stackoverflow.com/questions/62150333/lateinit-property-mock-object-has-not-been-initialized

Mockを使う時は、BeforeEachアノテーションで MockitoAnnotations.openMocks(this)をしないとダメだとのこと
@Mcokだけ書いとけば、@Injectみたいに初期化処理をしてくれるだろうと思ってましたが、そんなことはありませんでした。。

ドキュメントに記載がある通り、BeforeとAfterアノテーションでの設定が必要でした。
https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/MockitoAnnotations.html
ちなみに、ドキュメントではMockitoAnnotations.openMocksに1つずつ@Mockした変数を設定しているが、引数にthisと指定してあげるだけで、@Mockがついた変数の初期化をしてくれました。

しばらく、PHPerエンジニアでアノテーションにあまり触れる機会がなかったですが、今回の事象で久々にJavaのアノテーションを使ってたことを思い出したのと、改めてドキュメントを読むのは大切だと思いました。


株式会社fundbookについてはこちらから

https://fundbook.co.jp/corporate/

M&Aサービスについてはこちらから

https://fundbook.co.jp/

株式会社fundbook

Discussion