必要な状況
テスト対象クラスから呼ばれるクラスの一部のメソッドのみMock化したい場合
JMockit
1.46
結論
- ExpectationsにMock化したいインスタンスを渡して、Mock化したいメソッドのみExpectations内に定義する
- テスト対象クラスに(無理やり)設定する
テスト対象クラス
public class Sample {
@Inject
Hoge hoge;
public String getHoge() {
return hoge.getHoge() + hoge.getHogeUpper();
}
}
テスト対象クラスからInjectしたHogeのメソッドを使用しています。
そのHogeについて部分Mockしたい場合の対処法となります。
テスト対象クラスから呼ばれるクラス
public class Hoge {
public String getHoge() {
return "hoge";
}
public String getHogeUpper() {
return null;
}
}
getHogeは実装完了しており、getHogeUpperはまだ未実装という状況としてください。
テスト失敗
public class SampleTest {
@Tested
Sample sample;
@Injectable
Hoge hoge;
/**
* テスト対象クラスから呼ばれるクラスの一部のメソッドのみMock化したいケース。
* テストは失敗する。
*/
@Test
public void test() {
new Expectations(hoge) {{
hoge.getHogeUpper();
result = "HOGE";
}};
String actual = sample.getHoge();
assertThat(actual, is("hogeHOGE")); // actualはnullHOGEとなる
}
}
テスト対象クラスはInjectを使用しているため、通常、JMockitの@Testedと@Injectableでインスタンスを注入するようにかくと思いますが、それではテストは失敗します。
理由は、@InjectableでHogeのすべてのメソッドが空になるためです。
テスト成功
public class SampleTest2 {
Sample sample = new Sample();
/**
* テスト対象クラスから呼ばれるクラスの一部のメソッドのみMock化したいケース。
* テストは成功する。
* @throws Exception
* @throws NoSuchFieldException
*/
@Test
public void test() throws NoSuchFieldException, Exception {
Hoge hoge = new Hoge();
new Expectations(hoge) {
{
hoge.getHogeUpper();
result = "HOGE";
}
};
// Sampleのprivateフィールドに部分Mockしたhogeを設定
Field field = sample.getClass().getDeclaredField("hoge");
field.setAccessible(true);
field.set(sample, hoge);
String actual = sample.getHoge();
assertThat(actual, is("hogeHOGE"));
}
}
部分Mockの詳細は以下を参照してください。
https://jmockit-ja.nyamikan.net/tutorial/Mocking.html#partial
部分MockしたMockインスタンスを、リフレクションでテスト対象クラスに設定することでテストを成功させています。
最後に
JMockitの古いバージョンでは、MockUpで一部のみMock化し、getMockInstanceで取得したインスタンスをDeencapsulation.setFieldでテストクラスに設定していましたが、1.46ではどちらのメソッドも消えていました。。。
JMockitはAPIの削除が頻繁に行われているので、バージョンを新しくする際はご注意ください。
他に良い方法などあればぜひ教えて下さい。