Mockitoがstaticメソッドのモックに対応したので試してみた
いつの間にかMockitoがstaticメソッドのモックに対応したので試してみました。
概要
以前のMockitoはstaticなメソッドのモックに対応しておらずPowerMock
を使ったりする必要がありましたが、Mockitoの3.4.0からstaticメソッドのモックに対応したらしいです。
依存への追加
3.4.0以降のMockitoを追加すればOKです。
testImplementation 'org.mockito:mockito-core:3.4.5'
testImplementation 'org.mockito:mockito-inline:3.4.5'
ただしmockito-coreだけではなくmockito-inlineも依存に追加する必要があります。
使うクラスはmockito-coreで定義されているのですが実行するのにmockito-inlineも必要らしくmockito-inline無しで実行するとエラーになります。mockito-inline自体の依存にmockito-coreが含まれるので本来であればmockito-inlineのみでも大丈夫なのですが、SpringのアプリケーションでSpringのBOMとか読み込んでいる場合はそちらに記述されているバージョンが優先されてmockito-coreだけ低いバージョンになったりするので注意が必要です。
実装例
Instant.now()をモックにして任意の時刻を返すようにした例です。
@Test
public void mockTest() throws Exception {
    var ret = Instant.ofEpochSecond(0);
    var mocked = Mockito.mockStatic(Instant.class);
    mocked.when(Instant::now).thenReturn(ret);
    var actual = Instant.now();
    assertEquals(actual.toString(), "1970-01-01T00:00:00Z");  // OK
    mocked.verify(Instant::now);
    mocked.close();
}
まずMockito.mockStatic()にモックにしたいstaticメソッドがあるクラスを指定してモックコントローラを受け取ります。
あとはモックコントローラを使ってmocked.when(...).thenReturn(...)みたいな感じでいつものノリで設定すればOKです。
一点だけ注意が必要で最後にclose()のメソッドを呼ぶ必要があるらしく、忘れて別のテストケースで再度モックにしようとするとエラーになります。
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-resources で書くのがオススメらしくモックが適応される範囲が限定できて最後に自動でclose()も呼んでくれるので良いみたいです。
try (var mocked = Mockito.mockStatic(Instant.class)) {
    // 中だけモックが有効
}