JMockit101
1. 序章
この記事では、モッキングツールキットJMockitを中心とした新しいシリーズを開始します。
この最初の記事では、JMockitとは何か、その特性、およびモックがどのように作成され、使用されるかについて説明します。
後の記事では、その機能に焦点を当てて詳しく説明します。
2. JMockit
2.1. 序章
まず、JMockitとは何かについて説明しましょう。テストでオブジェクトをモックするためのJavaフレームワークです(JUnitとTestNGの両方に使用できます)。
JavaのインストルメンテーションAPIを使用して、実行時にクラスのバイトコードを変更し、クラスの動作を動的に変更します。 その長所のいくつかは、その表現力と、静的メソッドとプライベートメソッドを模倣するためのすぐに使える機能です。
たぶんあなたはJMockitに不慣れですが、それは間違いなくそれが新しいからではありません。 JMockitの開発は2006年6月に開始され、最初の安定したリリース日は2012年12月であるため、しばらく前から存在しています(記事の執筆時点での現在のバージョンは1.24です)。
2.2. Mavenの依存関係
まず、jmockit依存関係をプロジェクトに追加する必要があります。
<dependency>
<groupId>org.jmockit</groupId>
<artifactId>jmockit</artifactId>
<version>1.41</version>
</dependency>
2.3. 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.4. 記録-再生-検証モデル
JMockitを使用したテストは、記録、再生、検証の3つの段階に分けられます。
- record フェーズでは、テストの準備中、および実行するメソッドを呼び出す前に、次のステージで使用されるすべてのテストの予想される動作を定義します。
- replay フェーズは、テスト対象のコードが実行されるフェーズです。 前のステージで以前に記録されたモックされたメソッド/コンストラクターの呼び出しが再生されるようになります。
- 最後に、 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()メソッドでCollaborationorを使用するPerformerクラスをテストします。 このperform()メソッドは、 Model オブジェクトをパラメーターとして受け取り、そこから文字列を返す getInfo()を使用します。この文字列が渡されます。この特定のテストに対してtrueを返すCollaboratorからのcollaborate()メソッドに、この値は
したがって、テストされたクラスは次のようになります。
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について詳しく知りたい場合は、今後の記事にご注目ください。
このチュートリアルの完全な実装は、GitHubプロジェクトにあります。
4.1. シリーズの記事
シリーズのすべての記事: