[Mockito] 3.2.4 → 3.3.xにアップデートしたら(イケテナイところが)エラーになった件


Mockito 3.2.4から3.3.x(3.3.3)にアップデートしたら・・・スタブを設定しているコードが冗長?だったことが原因でエラーになってしまいました。

どんなコードだったの?

こんなコードでした。

when(rs.getInt("column")).thenReturn(100);      
assertEquals(Integer.valueOf(100), TYPE_HANDLER.getResult(rs, "column"));

when(rs.getInt("column")).thenReturn(0);
assertEquals(Integer.valueOf(0), TYPE_HANDLER.getResult(rs, "column"));

どんなエラーになったの?

こんなエラーです。

org.mockito.exceptions.misusing.UnnecessaryStubbingException: 
Unnecessary stubbings detected.
Clean & maintainable test code requires zero unnecessary code.
There are 1 unnecessary stubbing (click to navigate to relevant line of code):
  1. -> at org.apache.ibatis.type.YearTypeHandlerTest.shouldGetResultFromResultSetByName(YearTypeHandlerTest.java:44)
Please remove unnecessary stubbings or use 'lenient' strictness. More info: javadoc for UnnecessaryStubbingException class.

何がイケテナイの?

どうやら・・・thenReturn のところがイケテナイようで、以下のようにしたらエラーがなくなりました。

when(rs.getInt("column")).thenReturn(100, 0); // 一箇所に集約!!
assertEquals(Integer.valueOf(100), TYPE_HANDLER.getResult(rs, "column"));
assertEquals(Integer.valueOf(0), TYPE_HANDLER.getResult(rs, "column"));

まーね・・・という感じです。

コードを直さないとダメなの?

明確な意図がない場合は基本的にはコードを直した方がよいと思いますが、エラーメッセージにも記載があるように「lenient」モードにすることでこのエラーを抑止することができます。以下に、抑止する方法をいくつか紹介します。

NOTE:

ざっと調べた範囲で見つけた抑止方法なので、他にももっとよい方法があるかもしれません!!

@Mocklenient 属性の利用

アノテーションを利用してモックオブジェクトを生成している場合、アノテーションの属性指定で抑止可能です。

// @Mock
@Mock(lenient = true)
protected ResultSet rs;

MockSettings#lenient() の利用

Mockito#mock メソッドを利用してモックオブジェクトを生成している場合は、メソッドのオプション指定で抑止可能です。

// ResultSet rs = mock(ResultSet.class);
ResultSet rs = mock(ResultSet.class, withSettings().lenient());
when(rs.getInt("column")).thenReturn(INSTANT.getValue());
assertEquals(INSTANT, TYPE_HANDLER.getResult(rs, "column"));
when(rs.getInt("column")).thenReturn(0);
assertEquals(Year.of(0), TYPE_HANDLER.getResult(rs, "column"));

Mockito.lenient() の利用

Mockito.lenient() を使用して抑止することもできますが、この方法で抑止するくらいなら、ちゃんと直しちゃった方がよいでしょう。

// when(rs.getInt("column")).thenReturn(100);
lenient().when(rs.getInt("column")).thenReturn(100);
// ...

JUnit 5編: @MockitoSettingsstrictness 属性の利用

JUnit 5(MockitoExtension)を利用している場合は、@MockitoSettings アノテーションの属性指定で抑止可能です。
この設定は、Mockito#mock メソッドを使用している部分にも適用されるみたいです。

@MockitoSettings(strictness = Strictness.LENIENT) // これを追加
@ExtendWith(MockitoExtension.class)
abstract class BaseTypeHandlerTest {
  // ...
  @Mock
  protected PreparedStatement ps;
  // ...
}

NOTE:

本エントリでは触れませんが、JUnit 4でも同様のオプションが用意されているみたいです。

まとめ

Mickitoは単体テストの前提を整えるのに非常に便利なライブラリですが、それらのコードをクリーンな状態を保たないと後から見た時に「これ何やってるんだっけ?」みたいな状態になりがちなので・・・こういったチェック機構はありがたいですね(直し方がわからない時がたまにありますが・・・)。
ただ・・・ライブラリのレポート内容が絶対的に正しいとは限らないので、レポート内容とコードを見極めたうえで、必要に応じて「lenient」モードの利用も検討するとよいと思います。