Mockitoのテストをクリーンに保つための機能

はじめに

こんにちは、エキサイト株式会社でインターンをさせていただいている山内です。 今回はMockitoを使ってユニットテストを書いていた際に遭遇したUnnecessaryStubbingExceptionというエラーについてご紹介します。

状況

SpringBootでのサンプルコードは以下のようになっています。

// SampleRepository.java
public interface SampleRepository {
    Boolean hoge();

    Boolean fuge();
}
// SampleService.java
public interface SampleService {
    Boolean hoge();
}
// SampleServiceImpl.java
@Service
@RequiredArgsConstructor
public class SampleServiceImpl implements SampleService {
    private final SampleRepository sampleRepository;

    @Override
    public Boolean hoge() {
        return sampleRepository.hoge();
    }
}
// SampleServiceImplTest.java
@ExtendWith(MockitoExtension.class)
public class SampleServiceImplTest {
    @Mock
    private SampleRepository sampleRepository;

    @InjectMocks
    private SampleServiceImpl sampleService;

    @Test
    @DisplayName("サンプルテスト")
    public void sampleTest() {
        Mockito
                .when(sampleRepository.hoge())
                .thenReturn(true);

        Mockito
                .when(sampleRepository.fuge())
                .thenReturn(true);

        Assertions.assertEquals(true, sampleService.hoge());
    }
}

テストを実行すると、下記のようなエラーが出ます。

org.mockito.exceptions.misusing.UnnecessaryStubbingException: 
Unnecessary stubbings detected.
Clean & maintainable test code requires zero unnecessary code.

原因

エラーログの通り、不要なスタブが定義されていることが原因です。

今回の例で言うと、SampleRepository.javaではhoge()とfuge()というメソッドが定義されていますが、SampleService.javaで使われているのはSampleRepository.hoge()のみです。つまり、SampleRepositoryImplTest.javaで定義されている以下の部分は今回テストしたいSampleService.hoge()では使われていないため不要なスタブとして検出されているのです。

Mockito
        .when(sampleRepository.fuge())
        .thenReturn(true);

スタブとは

スタブとは、テストしたい物とは別のメソッドの振る舞いを定義してあげるものです。つまり、テストしたいメソッド内で他のメソッドを呼んでいる場合に他のメソッドの動作を保証しなくてもテストが書け、それぞれのメソッドごとに動作を保証することができるというものです。

解決方法

解決方法はシンプルで、以下のように不要なスタブを消してあげるだけです。

// SampleServiceImplTest.java
@ExtendWith(MockitoExtension.class)
public class SampleServiceImplTest {
    @Mock
    private SampleRepository sampleRepository;

    @InjectMocks
    private SampleServiceImpl sampleService;

    @Test
    @DisplayName("サンプルテスト")
    public void sampleTest() {
        Mockito
                .when(sampleRepository.hoge())
                .thenReturn(true);

//        Mockito
//                .when(sampleRepository.fuge())
//                .thenReturn(true);

        Assertions.assertEquals(true, sampleService.hoge());
    }
}

その他の解決方法もあり、こちらのMockitoブログで言及されています。

なぜこのようなエラーを出すのか

このようなエラーを出している理由は、テストを読みやすく、クリーンに保つためです。 テストをクリーンに保つことで、そのテストで本当にしたいことが明確になり、見通しの良いコードになると思います。

最後に

このインターンを通して、「良いコードを書く」ということはとても難しく、日々意識して書いていくことで身に付いていくものだと実感しています。今回ご紹介したMockitoの不要なスタブの検証もその一歩になるのではないかと思っています。

まだまだ未熟ですが、これからも良いコードを意識して書いていきたいと思います!

最後までお読みいただきありがとうございました!