エンターテイメント!!

遊戯王好きのJavaエンジニアのブログ。バーニングソウルを会得する特訓中。

mockitoでstaticメソッドをモック化したら、他のstaticメソッドがnull返してくる

経緯

mockito使って、staticメソッドを使っている箇所をモック化したのだが、他のstaticメソッドを呼んだら、nullが帰ってきた。
実装は、nullが返ってくることはないはずなんだが、なぜか帰ってきていて、悩んだので、メモ。

原因

簡単に言うと、モック化することで、他のstaticメソッドもモック化されて、nullが返ってきていた。

自分がモック化したのは、下記のコード

MockedStatic<LocalDate> mock = Mockito.mockStatic(LocalDate.class);
mock.when(LocalDate::now).thenReturn(testDate);

上のやつだと、たしかにnow()は、用意したテストデータ返してくれるけど、他のstaticメソッドを呼んだときに、nullを返す。
内部の実装追ってないけど、おそらく、ラッピングしたやつが設定されて、他のstaticメソッドは、から実装(null返すだけ)になってるんじゃないかと思う。

対処

mockStaticの第二引数に、Mockito.CALLS_REAL_METHODSを設定する。
原因のところに書いたソースを直すと、以下の感じになる。

MockedStatic<LocalDate> mock = Mockito.mockStatic(LocalDate.class, Mockito.CALLS_REAL_METHODS);
mock.when(LocalDate::now).thenReturn(testDate);

第二引数に、Mockito.CALLS_REAL_METHODSを設定することで、モック化前の実装が呼び出されるらしい。

で、うまく動いたのだが、まだ罠があることをこのときの俺は知らなかった。。。

トラップ発動

テストケースうまく動いたから、テストケース全体を動かしたら、なぜかエラーに。。。

For java.time.Instant, static mocking is already registered in the current thread

To create a new mock, the existing static mock registration must be deregistered
org.mockito.exceptions.base.MockitoException: 
For java.time.Instant, static mocking is already registered in the current thread

トラップ解除

理由は、モック化したものが残ったままだと、別なところでモック化したときにエラーになったから。

対処方としては、モック化が終わったら、クローズする必要があるらしい。
オートクローズに対応しているので、try-with-resourse使って閉じるのがいいらしい。

    try (MockedStatic<LocalDate> mock = Mockito.mockStatic(LocalDate.class,
        Mockito.CALLS_REAL_METHODS)) {
// テストの実装
    }

モックの有効範囲も明確化するので、俺もtry-with-resourseで自動クローズさせるのが良いと思いました。

雑記

トラップ発動がウザすぎる。。。
解決したと思って、全テストケース回してコケたときの絶望感が半端なかった。

神は俺に試練与えるの好きすぎだろ。
絶対にサディストだ。
特に、トイレが確保できないところでの便意は、まじで辞めて欲しい。
最近、リモート作業しないことが増えてきて、たまに電車のなかで便意が発動するのが、そうとう厄介。

参考サイト

https://nainaistar.hatenablog.com/entry/2022/07/18/120000 https://kamoqq.info/post/mockito-static-method-mock/