JMockit 101

JMockit 101

1. 前書き

この記事では、モッキングツールキットJMockitを中心とした新しいシリーズを開始します。

この最初の記事では、JMockitとは何か、その特性、およびモックがどのように作成され、使用されるかについて説明します。

後の記事では、その機能に焦点を当て、さらに深く掘り下げます。

2. JMockit

2.1. 前書き

まず、JMockitとは何かについて説明しましょう。テストでオブジェクトをモックするためのJavaフレームワークです(JUnitTestNGの両方に使用できます)。

JavaのインストルメンテーションAPIを使用して、実行時にクラスのバイトコードを変更し、クラスの動作を動的に変更します。 その長所のいくつかは、その表現可能性と、静的メソッドおよびプライベートメソッドをモックするためのすぐに使える機能です。

JMockitを初めて使用する場合もありますが、それは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つの段階に分かれています。

  1. recordフェーズでは、テストの準備中、および実行するメソッドを呼び出す前に、次のステージで使用されるすべてのテストの予想される動作を定義します。

  2. replayフェーズは、テスト対象のコードが実行されるフェーズです。 前のステージで以前に記録された、モックされたメソッド/コンストラクターの呼び出しが再生されます。

  3. 最後に、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()メソッドは、パラメーターとしてModelオブジェクトを受け取り、そこから文字列を返すgetInfo()を使用します。この文字列は、%からcollaborate()メソッドに渡されます。 (t7)sは、この特定のテストに対して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について詳しく知りたい場合は、今後の記事をお楽しみに。

このチュートリアルの完全な実装は、the GitHub project.にあります。

4.1. シリーズの記事

シリーズのすべての記事: