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]
この場合は、以下のように@SpyBean
のname
属性にスーパークラス名を入れればOKです。
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class SampleTest {
@SpyBean(name = "superClass")
SuperClass superClass;
@Test
public void sampleTest() {
doNothing().when(superClass).superClassMethod();
}
}