EasyMockの紹介

[[1-intro]]

1前書き

過去に、私たちは JMockit Mockito について広く話しました。

このチュートリアルでは、別のモックツール - EasyMock の紹介をします。

[[2-maven-dependencies]]

2 Mavenの依存関係

ダイビングする前に、 pom.xml に次の依存関係を追加しましょう。

<dependency>
    <groupId>org.easymock</groupId>
    <artifactId>easymock</artifactId>
    <version>3.5.1</version>
    <scope>test</scope>
</dependency>

最新版は、常にhttps://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22org.easymock%22%20AND%20a%3A%22easymock%22[here]にあります。

[[3-core-concepts]]

3コアコンセプト

モックを生成するとき、ターゲットオブジェクトをシミュレートし、その振る舞いを指定し、最後にそれが期待通りに使われているかどうかを検証することができます

EasyMockのモックを扱うには4つのステップがあります。

  1. ターゲットクラスのモックを作成する

  2. アクション、結果を含む、その予想される動作を記録します.

例外など

  1. テストでモックを使う

  2. 予想通りに動作しているかどうかを確認する

レコーディングが終了したら、それを「再生」モードに切り替えます。そのため、モックは、それを使用する予定のオブジェクトとコラボレーションするときにレコーディングされたとおりに動作します。

最終的に、私たちはすべてが期待通りに動くかどうかを検証します。

上記の4つのステップは、http://easymock.org/api/org/easymock/EasyMock.html[ org.easymock.EasyMock ]のメソッドに関連しています。

具体的なクラスであろうとなかろうと、ターゲットクラスのモックを生成します。 インタフェース。作成されると、モックは「レコーディング」モードになります。 EasyMockはモックオブジェクトが行ったすべてのアクションを記録し、それらを再生します 「再生」モード 。 expect(…​) :

この方法では、電話、結果、 関連する記録アクションの例外 。 replay(…​) :

指定されたモックを「再生」モードに切り替えます。その後、任意の行動の誘発 以前に記録されたメソッド呼び出しは「記録された結果」を再生します 。 verify(…​) :

すべての期待が満たされていること、およびモックで予期しない呼び出しが行われていないことを確認します。

次のセクションでは、実際の例を使用して、これらの手順が実際にどのように機能するかを説明します。

[[4-practical-example]]

4モッキングの実例

先に進む前に、文脈の例を見てみましょう。Baeldungのブログの読者がいるとしましょう。

次のモデルを作成することから始めましょう。

public class BaeldungReader {

    private ArticleReader articleReader;
    private IArticleWriter articleWriter;

   //constructors

    public BaeldungArticle readNext(){
        return articleReader.next();
    }

    public List<BaeldungArticle> readTopic(String topic){
        return articleReader.ofTopic(topic);
    }

    public String write(String title, String content){
        return articleWriter.write(title, content);
    }
}

このモデルでは、 articleReader (具象クラス)と articleWriter (インタフェース)の2つのプライベートメンバがあります。

次に、 BaeldungReader の動作を確認するためにそれらを偽造します。

[[5-mock-with-java-code]]

5 Javaコードでモックする

ArticleReader を偽装することから始めましょう。

[[51-typical-mocking]]

5.1. 典型的なモッキング

読者が記事を読み飛ばすときに articleReader.next() メソッドが呼び出されることを期待します。

@Test
public void whenReadNext__thenNextArticleRead(){
    ArticleReader mockArticleReader = mock(ArticleReader.class);
    BaeldungReader baeldungReader
      = new BaeldungReader(mockArticleReader);

    expect(mockArticleReader.next()).andReturn(null);
    replay(mockArticleReader);

    baeldungReader.readNext();

    verify(mockArticleReader);
}

上記のサンプルコードでは、4ステップの手順に厳密に従い、 ArticleReader クラスをモックします。

  • mockArticleReader.next() が何を返すかはあまり気にしませんが、 expect(…​)。andReturn(…​). を使用して mockArticleReader.next() ** の戻り値を指定する必要があります。

expect(…​) では、EasyMockはメソッドが値を返すか__Exceptionをスローすることを期待しています。

単純にすると:

mockArticleReader.next();
replay(mockArticleReader);

メソッドが何かを返す場合、EasyMockは expect(…​)。andReturn(…​) を呼び出す必要があるので、これについて不満を言います。

それが void メソッドである場合、http://easymock.org/api/org/easymock/EasyMock.html#expectLastCall--[ expectLastCall() ]を使用してそのアクションを expect できます

mockArticleReader.someVoidMethod();
expectLastCall();
replay(mockArticleReader);

[[52-mocking-strictness]]

5.2. 再生順序

アクションを特定の順序で再生する必要がある場合は、さらに厳密にすることができます。

@Test
public void whenReadNextAndSkimTopics__thenAllAllowed(){
    ArticleReader mockArticleReader
      = strictMock(ArticleReader.class);
    BaeldungReade baeldungReader
      = new BaeldungReader(mockArticleReader);

    expect(mockArticleReader.next()).andReturn(null);
    expect(mockArticleReader.ofTopic("easymock")).andReturn(null);
    replay(mockArticleReader);

    baeldungReader.readNext();
    baeldungReader.readTopic("easymock");

    verify(mockArticleReader);
}

このスニペットでは、 strictMock(…​) を使用してメソッド呼び出しの順序を確認しています。 mock(…​) および strictMock(…​) によって作成されたモックの場合、予期しないメソッド呼び出しが発生すると AssertionError が発生します。

  • モックに対するメソッド呼び出しを許可するには、 niceMock(…​) を使用できます。

@Test
public void whenReadNextAndOthers__thenAllowed(){
    ArticleReader mockArticleReader = niceMock(ArticleReader.class);
    BaeldungReade baeldungReader = new BaeldungReader(mockArticleReader);

    expect(mockArticleReader.next()).andReturn(null);
    replay(mockArticleReader);

    baeldungReader.readNext();
    baeldungReader.readTopic("easymock");

    verify(mockArticleReader);
}

ここでは baeldungReader.readTopic(…​) が呼び出されることを想定していませんでしたが、EasyMockは文句を言っていませんでした。 niceMock(…​)を使用すると、 EasyMockはターゲットオブジェクトが期待される動作を実行したかどうかだけを気にするようになりました。

[[53-mock-throwable]]

5.3. モック__Exception - がスローされます

それでは、引き続き IArticleWriter インターフェースのモックを作成し、予想される Throwables の処理方法を説明しましょう。

@Test
public void whenWriteMaliciousContent__thenArgumentIllegal() {
   //mocking and initialization

    expect(mockArticleWriter
      .write("easymock","<body onload=alert('baeldung')>"))
      .andThrow(new IllegalArgumentException());
    replay(mockArticleWriter);

   //write malicious content and capture exception as expectedException

    verify(mockArticleWriter);
    assertEquals(
      IllegalArgumentException.class,
      expectedException.getClass());
}

上記のスニペットでは、 articleWriter がhttps://www.owasp.org/index.php/Cross-site Scripting (XSS)[XSS(Cross-site Scripting)]攻撃を検出するのに十分堅牢であることを期待しています。

そのため、読者が記事のコンテンツに悪質なコードを挿入しようとすると、作者は IllegalArgumentException をスローするはずです。この期待される動作を expect(…​)。andThrow(…​) を使って記録しました。

[[6-mock-with-annotation]]

6. 注釈付きのモック

EasyMockはアノテーションを使ったモックの注入もサポートしています。 それらを使用するには、http://easymock.org/api/org/easymock/EasyMockRunner.html を使用してユニットテストを実行し、http://easymock.org/api/org/を処理する必要があります。 easymock/Mock.html[ @ Mock ]およびhttp://easymock.org/api/org/easymock/TestSubject.html[ @TestSubject__]注釈。

以前のスニペットを書き換えましょう。

@RunWith(EasyMockRunner.class)
public class BaeldungReaderAnnotatedTest {

    @Mock
    ArticleReader mockArticleReader;

    @TestSubject
    BaeldungReader baeldungReader = new BaeldungReader();

    @Test
    public void whenReadNext__thenNextArticleRead() {
        expect(mockArticleReader.next()).andReturn(null);
        replay(mockArticleReader);
        baeldungReader.readNext();
        verify(mockArticleReader);
    }
}

mock(…​) と同等の、モックは @ Mock のアノテーションが付けられたフィールドに挿入されます。そしてこれらのモックは、 @ TestSubject でアノテーションが付けられたクラスのフィールドに注入されます。

上記のコードでは、 baeldungReaderの articleReader__フィールドを明示的に初期化しませんでした。

これは、 mockArticleReader がarticleReader__フィールドに挿入されたためです。

  • EasyMockRunner の代わりに別のテストランナーを使用したい場合は、JUnitテストルールhttp://easymock.org/api/org/easymock/EasyMockRule.html[ EasyMockRule ]:** を使用できます

public class BaeldungReaderAnnotatedWithRuleTest {

    @Rule
    public EasyMockRule mockRule = new EasyMockRule(this);

   //...

    @Test
    public void whenReadNext__thenNextArticleRead(){
        expect(mockArticleReader.next()).andReturn(null);
        replay(mockArticleReader);
        baeldungReader.readNext();
        verify(mockArticleReader);
    }

}

[[7-mock-with-easymocksupport]]

7. EasyMockSupport でモックする

1回のテストで複数のモックを導入する必要がある場合があり、手動で繰り返す必要があります。

replay(A);
replay(B);
replay(C);//...
verify(A);
verify(B);
verify(C);

これは醜いです、そして私たちは優雅な解決策を必要としています。

幸いなことに、これに対処するためにEasyMock EasyMockSupport というクラスがあります。これは、モックを追跡するのに役立ちます。これにより、次のようなバッチでそれらを再生して検証できます。

----//...
public class BaeldungReaderMockSupportTest extends EasyMockSupport{
//...
@Test
public void whenReadAndWriteSequencially__thenWorks(){
    expect(mockArticleReader.next()).andReturn(null)
      .times(2).andThrow(new NoSuchElementException());
    expect(mockArticleWriter.write("title", "content"))
      .andReturn("BAEL-201801");
    replayAll();
//execute read and write operations consecutively
verifyAll();
    assertEquals(
      NoSuchElementException.class,
      expectedException.getClass());
    assertEquals("BAEL-201801", articleId);
}

}

ここでは、__articleReader__と__articleWriter__の両方をモックしました。これらのモックを「再生」モードに設定するときは、__EasyMockSupport__が提供する静的メソッドhttp://easymock.org/api/org/easymock/EasyMockSupport.html#replayAll--[__replayAll()__]を使用し、httpを使用しました。//easymock.org/api/org/easymock/EasyMockSupport.html#verifyAll--[__verifyAll ()__]]をクリックして、動作をバッチで確認します。

また、__expect__フェーズでhttp://easymock.org/api/org/easymock/IExpectationSetters.html#times-int-[__times(...)__]メソッドを導入しました。コードの重複を避けるために、メソッドが呼び出される回数を指定するのに役立ちます。

委任を通じて__EasyMockSupport__を使用することもできます。

[source,java,gutter:,true]

EasyMockSupport easyMockSupport = new EasyMockSupport();

@Test public void whenReadAndWriteSequencially__thenWorks(){ ArticleReader mockArticleReader = easyMockSupport .createMock(ArticleReader.class); IArticleWriter mockArticleWriter = easyMockSupport .createMock(IArticleWriter.class); BaeldungReader baeldungReader = new BaeldungReader( mockArticleReader, mockArticleWriter);

expect(mockArticleReader.next()).andReturn(null);
expect(mockArticleWriter.write("title", "content"))
  .andReturn("");
easyMockSupport.replayAll();
baeldungReader.readNext();
baeldungReader.write("title", "content");
    easyMockSupport.verifyAll();
}
以前は、モックを作成および管理するために静的メソッドまたはアノテーションを使用していました。内部では、これらの静的および注釈付きのモックはグローバルな__EasyMockSupport__インスタンスによって制御されています。

ここでは、明示的にインスタンス化し、委任を通じてこれらすべてのモックを自分の管理下におきます。 EasyMockとのテストコードに名前の競合がある場合、または同様のケースがある場合は、これによって混乱を避けることができます。

[[8-summary]]

===  **  8結論**

この記事では、EasyMockの基本的な使用法、モック・オブジェクトの生成方法、それらの動作の記録と再生、およびそれらが正しく動作しているかどうかの検証方法について簡単に紹介しました。

興味があるかもしれない場合は、EasyMock、Mocket、そしてJMockitの比較のためにリンク:/mockito-vs-easymock-vs-jmockit[この記事]をチェックしてください。

いつものように、完全な実装はhttps://github.com/eugenp/tutorials/tree/master/testing-modules/mocks/mock-comparisons[Githubに追加]を見つけることができます。