Mockitoでstaticメソッドとfinalクラスをモック化する
相変わらず苦手なユニットテスト作成。
特にライブラリの呼び出し部分をモック化するのが大変だなと思う今日この頃。
モック化自体は Mockito が便利なのですが、モック化したいものが以下のようなものだとちょっと面倒ですよね。
・private メソッド
・static メソッド
・final クラス
今回は、この中から static メソッドと final クラスのモック化について紹介します。
Mockito
Mockito は Java のユニットテストのために開発されたモックフレームワークで、Kotlin で開発している時にもお世話になりました。
Kotlin の場合は Mockito-Kotlin を使う感じですね。
今回は Java になりますが、JUnit と Mockito を以下のバージョンで利用していきます。
Junit: 5.4.2
Mockito: 4.4.0
staticメソッドのモック化
static メソッドはクラスをインスタンス化せずに利用できるので、以下のように Mock アノテーションでインスタンス化したものが使えないなと悩んでいました。
@Mock
private Hoge hoge;
調べてみると、Mockito の以下のクラスを利用すれば static のメソッドをモック化することが可能なようです。
MockedStatic
Mockito.mockStatic
Hoge クラスの中身は以下の内容だと仮定します。
public class Hoge {
public static boolean isExpired() {
return false;
}
}
また static メソッドをモック化する方法は大きく 2 種類ありますが、私は try を使う方が好みです。
この辺はテストロジック次第というところもありますが、スコープが明確になってわかりやすいかなっと。
Hoge.isExpired() がモック化されているのは try の中だけになります。
try (MockedStatic<Hoge> mocked = mockStatic(Hoge.class)) {
mocked.when( () -> Hoge.isExpired() )
.thenReturn(true);
assertEquals(true, Hoge.isExpired());
}
try を使わない場合は、利用後に close() の呼び出しが必要です。
こちらも、Hoge.isExpired() がモック化されているのは close() の呼び出しまで。
MockedStatic<Hoge> mocked = mockStatic(Hoge.class);
mocked.when( () -> Hoge.isExpired() ).thenReturn(true);
assertEquals(true, Hoge.isExpired());
mocked.close();
finalクラスをモック化する
次に final クラスのモック化です。
以下のような final クラス Fuga をモック化してみます。
public final class Fuga {
}
@Mock
private Fuga fuga;
いざビルドして test タスクを実行してみると・・・。
org.mockito.exceptions.base.MockitoException at MockitoExtension.java:153
これだけの情報だと何がなんだかわかりませんね。
もう少し詳細なエラー内容が欲しいのですが、コンソールに出力されないのですよね。
ちょっと古いですが、以下の記事と状況が似ていたので参考に。
うーん、以下のような詳細なエラー内容はどうやったら表示できるのだろうか。
Cannot mock/spy class xxxxxxxxxx
Mockito cannot mock/spy following:
– final classes
– anonymous classes
– primitive types
ということで、下記のファイルを用意します。
(テキストファイル形式で問題ありません)
resources 配下に mockito-extensions ディレクトリがない場合は作成してください。
src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
ファイルには 1 行だけ、以下を記載します。
これで、final クラスもモック化が可能となりました。
mock-maker-inline
まとめ
ちなみに、Mockito で利用していない when() があると、以下の Exception が発生します。
UnnecessaryStubbingException
これを解決するために参考になったのは以下のサイト。
うーん JUnit 含め、まだまだ Mockito には悩まされそうだなぁ。
JUnit5で「Please remove unnecessary stubbings or use ‘lenient’ strictness. More info: javadoc for UnnecessaryStubbingException class.」エラーが発生した場合は、クラスレベルのアノテーションに@MockitoSettings(strictness = Strictness.LENIENT)を付与すれば回避することが出来ます。
Spring Bootでmockitoを使ってテストする
Junit4 と 5 もそうですが、バージョンの組み合わせで挙動が変わるのは大変です・・・。