Help us understand the problem. What is going on with this article?

Spring スーパークラスのモック化

More than 1 year has passed since last update.

springでスーパークラスをモック化したテストが実行できず、しばらくはまってしまった件。
モック化するクラスを特定させる必要がありました。

失敗例

Spring Boot 2.0.5 で確認してます

継承関係のある2つのクラスを用意します。
これらは@Componentを付けて、DIコンテナに突っ込んでます。

スーパークラス

@Component
public class SuperClass {

    void superClassMethod() {
        System.out.println("スーパークラスのメソッドを呼び出しました。");
    }
}

サブクラス

@Component
public class SubClass extends SuperClass {

    void subClassMethod() {
        System.out.println("サブクラスのメソッドを呼び出しました。");
    }
}

@MockBeanでスーパークラスをモック化して使うテストを書きます。

テストコード

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class SampleTest {

    @MockBean
    SuperClass superClass;

    @Test
    public void sampleTest() {
        doNothing().when(superClass).superClassMethod();
    }
}

実行します。

テスト実行結果

Caused by: java.lang.IllegalStateException: Unable to register mock bean com.example.vendingmachine.sample.SuperClass expected a single matching bean to replace but found [subClass, superClass]

無事死亡。

何がダメなのか

例外メッセージを見ると、以下の記述があります。

expected a single matching bean to replace but found [subClass, superClass]

要するに「モック化対象がsubClassとsuperClassの2つあるため、どっちをモック化したらいいか分かんないです」ということ。
モック化対象クラスにサブクラスが存在する場合、そちらもモック化対象と判定されてしまう。

解決策

以下のように@Queliferを使って「スーパークラスをモック化するんだ!」と宣言すればよいです。

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class SampleTest {

    @MockBean
    @Qualifier("superClass")
    SuperClass superClass;

    @Test
    public void sampleTest() {
        doNothing().when(superClass).superClassMethod();
    }
}

@SpyBeanの場合

メソッド単位でモック化できる@SpyBeanの場合も同じ感じでエラーになります。

テストコード

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class SampleTest {

    @SpyBean
    SuperClass superClass;

    @Test
    public void sampleTest() {
        doNothing().when(superClass).superClassMethod();
    }
}

テスト実行結果

java.lang.IllegalStateException: No bean found for definition [SpyDefinition@f2b90fc name = '', typeToSpy = com.example.vendingmachine.sample.SuperClass, reset = AFTER]

この場合は、以下のように@SpyBeanname属性にスーパークラス名を入れればOKです。

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class SampleTest {

    @SpyBean(name = "superClass")
    SuperClass superClass;

    @Test
    public void sampleTest() {
        doNothing().when(superClass).superClassMethod();
    }
}

参考

SpringFrameworkのAutowiredでQualifierタグを利用

lightcafe_gr
全国にグループ会社を持つIT企業です
https://www.lightcafe.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away