1前書き
この記事では、モックツールキットhttp://jmockit.github.io[JMockit]を中心とした新しいシリーズを始めます。
この第1回目の記事では、JMockitとは何か、その特性、およびそれを使ってモックを作成して使用する方法について説明します。
今後の記事では、その機能に焦点を絞り、さらに詳しく説明します。
2 JMockit
2.1. 前書き
まずはじめに、JMockitとは何かについて説明しましょう。テストでオブジェクトをモックするためのJavaフレームワーク(http://junit.org/junit4/[JUnit]とhttp://testng.org/doc/の両方に使えますindex.html[TestNG]のもの)。
JavaのインスツルメンテーションAPIを使用して実行時にクラスのバイトコードを変更し、それらの動作を動的に変更します。その長所のいくつかは、その表現可能性と、静的メソッドとプライベートメソッドを模擬するための独創的な能力です。
たぶんあなたはJMockitに慣れていないかもしれませんが、それは間違いなくそれが新しいからではありません。 JMockitの開発は2006年6月に開始され、最初の安定版リリースは2012年12月までなので、今のところあります(現在のバージョンはこの記事の執筆時点では1.24です)。
2.2. JMockit
の表現力
前述のように、JMockitの最大の強みの1つは表現力です。モックAPIからメソッドを呼び出すのではなく、モックを作成してその動作を定義するには、それらを直接定義するだけです。
つまり、次のようなことはしないでください。
API.expect(mockInstance.method()).andThenReturn(value).times(2);
代わりに、
new Expectation() {
mockInstance.method();
result = value;
times = 2;
}
もっとコードだと思われるかもしれませんが、3行すべてを1行だけにすることができます。本当に重要なのは、連鎖したメソッド呼び出しの大きな「トレイン」ができないことです。代わりに、呼び出されたときのモックの動作を定義します。
result = value
の部分で何か(固定値、動的に生成された値、例外など)を返す可能性があることを考慮すると、JMockitの表現力はさらに明白になります。
2.3. 記録再生検証モデル
JMockitを使用したテストは3つの異なる段階に分けられます。
記録、再生、検証
-
record
フェーズでは、テスト準備中と
実行したいメソッドへの呼び出しでは、次の段階で使用されるすべてのテストで予想される動作を定義します。
-
再生
フェーズは、テスト中のコードが
実行されました。前のステージで以前に記録されたモックメソッド/コンストラクタの呼び出しが再生されます。
-
最後に、
verify
フェーズで、次の結果が
テストは私たちが期待したものでした(そしてモックは振る舞いそして記録段階で定義されたものに従って使われました)。
コード例では、テスト用のワイヤーフレームは次のようになります。
@Test
public void testWireframe() {
//preparation code not specific to JMockit, if any
new Expectations() {{
//define expected behaviour for mocks
}};
//execute code-under-test
new Verifications() {{
//verify mocks
}};
//assertions
}
3モックを作成する
3.1. JMockitのアノテーション
JMockitを使うとき、モックを使う最も簡単な方法はアノテーションを使うことです。
モックを作成するには3つ(
@ Mocked
、
@ Injectable
、および
@ Capturing
)、テスト中のクラスを指定するには1つ(
@ Tested
)があります。
フィールドに
@ Mocked
アノテーションを使用すると、その特定のクラスのすべての新しいオブジェクトのモックインスタンスが作成されます。
一方、
@ Injectable
アノテーションを使用すると、擬似インスタンスは1つだけ作成されます。
最後の注釈
@ Capturing
は
@ Mocked
のように動作しますが、範囲を注釈付きフィールドの型を拡張または実装するすべてのサブクラスに拡張します。
3.2. テストに引数を渡す
JMockitを使うとき、テストパラメータとしてモックを渡すことが可能です。これは、たとえば1つのテストだけに特定の動作を必要とする複雑なモデルオブジェクトのように、その1つのテスト専用のモックを作成するのに非常に役立ちます。それはこのようなものでしょう:
@RunWith(JMockit.class)
public class TestPassingArguments {
@Injectable
private Foo mockForEveryTest;
@Tested
private Bar bar;
@Test
public void testExample(@Mocked Xyz mockForJustThisTest) {
new Expectations() {{
mockForEveryTest.someMethod("foo");
mockForJustThisTest.someOtherMethod();
}};
bar.codeUnderTest();
}
}
APIメソッドを呼び出す代わりに、パラメーターとして渡すことでモックを作成するこの方法でも、最初から説明している表現力がわかります。
3.3. 完全な例
この記事を終わらせるために、JMockitを使ったテストの完全な例を紹介します。
この例では、
perform()
メソッドで
Collaborator
を使用する
Performer
クラスをテストします。この
perform()
メソッドは、Stringを返す
getInfo()
を使うパラメータとして
Model
オブジェクトを受け取ります。このStringは、
Collaborator
から
collaborate()
メソッドに渡され、この特定の
true
を返します。この値は
Collaborator
から
receive()
メソッドに渡されます。
したがって、テスト済みクラスは次のようになります。
public class Model {
public String getInfo(){
return "info";
}
}
public class Collaborator {
public boolean collaborate(String string){
return false;
}
public void receive(boolean bool){
//NOOP
}
}
public class Performer {
private Collaborator collaborator;
public void perform(Model model) {
boolean value = collaborator.collaborate(model.getInfo());
collaborator.receive(value);
}
}
そしてテストのコードは次のようになります。
@RunWith(JMockit.class)
public class PerformerTest {
@Injectable
private Collaborator collaborator;
@Tested
private Performer performer;
@Test
public void testThePerformMethod(@Mocked Model model) {
new Expectations() {{
model.getInfo();result = "bar";
collaborator.collaborate("bar"); result = true;
}};
performer.perform(model);
new Verifications() {{
collaborator.receive(true);
}};
}
}
4結論
これで、JMockitの実用的な紹介を締めくくります。 JMockitについてもっと知りたい場合は、今後の記事に注目してください。
このチュートリアルの完全な実装はhttps://github.com/eugenp/tutorials/tree/master/testing-modules/mocks[GitHubプロジェクトにあります。
4.1. シリーズの記事
シリーズのすべての記事:
-
リンク:/jmockit-101[JMockit 101]
-
リンク:/jmockit-expectations[JMockitのガイド – 期待]