web-dev-qa-db-ja.com

EasyMock対Mockito:設計と保守性?

これについて考える1つの方法は、コードの設計に関心がある場合、EasyMockは期待の概念によってフィードバックを提供するため、より良い選択です。

テストの保守性(読み取り、書き込みが容易で、変更によってあまり影響を受けない脆弱なテストが少ない)に関心がある場合は、Mockitoの方が適していると思われます。

私の質問は:

  • EasyMockを大規模なプロジェクトで使用したことがある場合、テストの維持が難しいと思いますか?
  • Mockitoの制限は何ですか(内部テスト以外)?
48
RAbraham

私はEasyMock開発者なので、少し部分的ですが、もちろん、大規模プロジェクトでEasyMockを使用しました。

私の意見では、EasyMockテストは実際にはたまに壊れるでしょう。 EasyMockは、あなたが期待するものの完全な記録を行うことを強制します。これにはある程度の訓練が必要です。テストされたメソッドが現在必要とするものではなく、期待されるものを実際に記録する必要があります。たとえば、モックでメソッドが呼び出される回数が問題ではない場合、andStubReturnの使用を恐れないでください。また、パラメータが気にならない場合は、anyObject()などを使用します。 TDDで考えることはそれを助けることができます。

私の分析では、EasyMockテストはより頻繁に中断しますが、Mockitoテストでは、必要なときに中断しません。私はテストを中断することを好みます。少なくとも私は自分の開発の影響が何であったかを知っています。もちろん、これは私の個人的な見解です。

29
Henri

これらのフレームワークのテストの可読性、サイズ、またはテスト手法については議論しませんが、それらは同等であると信じていますが、簡単な例で違いを示します。

与えられた:何かをどこかに格納する責任があるクラスがあります:

public class Service {

    public static final String PATH = "path";
    public static final String NAME = "name";
    public static final String CONTENT = "content";
    private FileDao dao;

    public void doSomething() {
        dao.store(PATH, NAME, IOUtils.toInputStream(CONTENT));
    }

    public void setDao(FileDao dao) {
        this.dao = dao;
    }
}

それをテストしたいと思います:

モッキート:

public class ServiceMockitoTest {

    private Service service;

    @Mock
    private FileDao dao;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        service = new Service();
        service.setDao(dao);
    }

    @Test
    public void testDoSomething() throws Exception {
        // given
        // when
        service.doSomething();
        // then
        ArgumentCaptor<InputStream> captor = ArgumentCaptor.forClass(InputStream.class);
        Mockito.verify(dao, times(1)).store(eq(Service.PATH), eq(Service.NAME), captor.capture());
        assertThat(Service.CONTENT, is(IOUtils.toString(captor.getValue())));
    }
}

EasyMock:

public class ServiceEasyMockTest {
    private Service service;
    private FileDao dao;

    @Before
    public void setUp() {
        dao = EasyMock.createNiceMock(FileDao.class);
        service = new Service();
        service.setDao(dao);
    }

    @Test
    public void testDoSomething() throws Exception {
        // given
        Capture<InputStream> captured = new Capture<InputStream>();
        dao.store(eq(Service.PATH), eq(Service.NAME), capture(captured));
        replay(dao);
        // when
        service.doSomething();
        // then
        assertThat(Service.CONTENT, is(IOUtils.toString(captured.getValue())));
        verify(dao);
    }
}

ご覧のとおり、両方のテストはかなり同じで、両方とも合格しています。ここで、他の誰かがサービスの実装を変更し、テストを実行しようとしていると想像してみましょう。

新しいサービスの実装:

dao.store(PATH + separator, NAME, IOUtils.toInputStream(CONTENT));

セパレーターがPATH定数の最後に追加されました

テスト結果は現在どのように見えるでしょうか?まず、両方のテストは失敗しますが、エラーメッセージは異なります。

EasyMock:

Java.lang.AssertionError: Nothing captured yet
    at org.easymock.Capture.getValue(Capture.Java:78)
    at ServiceEasyMockTest.testDoSomething(ServiceEasyMockTest.Java:36)
    at Sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at Sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.Java:39)

モッキート:

Argument(s) are different! Wanted:
dao.store(
    "path",
    "name",
    <Capturing argument>
);
-> at ServiceMockitoTest.testDoSomething(ServiceMockitoTest.Java:34)
Actual invocation has different arguments:
dao.store(
    "path\",
    "name",
    Java.io.ByteArrayInputStream@1c99159
);
-> at Service.doSomething(Service.Java:13)

EasyMockテストで何が起こりましたか、結果がキャプチャされなかったのはなぜですか? storeメソッドは実行されませんでしたが、ちょっと待ってください、EasyMockが嘘をついているのはなぜですか?

EasyMockが1つの行で2つの責任(スタブと検証)を混合しているためです。だから、何かがおかしいとき、どの部分が失敗の原因なのかを理解するのが難しいのです。

もちろん、あなたは私に言うことができます-アサーションの前にテストを変更して検証を移動するだけです。うわー、あなたは本気ですか、開発者はモックフレームワークによって強制されるいくつかの魔法の順序を覚えておくべきですか?

ところで、それは助けにはなりません:

Java.lang.AssertionError: 
  Expectation failure on verify:
    store("path", "name", capture(Nothing captured yet)): expected: 1, actual: 0
    at org.easymock.internal.MocksControl.verify(MocksControl.Java:111)
    at org.easymock.classextension.EasyMock.verify(EasyMock.Java:211)

それでも、メソッドは実行されなかったと言いましたが、それは別のパラメーターでのみ実行されました。

なぜMockitoの方が優れているのですか?このフレームワークは、2つの責任を1か所に混在させないため、テストが失敗すると、その理由を簡単に理解できます。

108

コードの設計に関心がある場合は、Easymockが期待の概念によってフィードバックを提供するため、より良い選択です。

面白い。私は、「期待の概念」により、多くの開発者がUnexpectedMethodCall問題を満たすためだけに、より多くの期待をテストに入れることがわかりました。それはデザインにどのように影響しますか?

コードを変更してもテストは中断しないはずです。機能が動作を停止すると、テストは中断します。コードの変更が発生したときにテストを中断したい場合は、Java file :)のmd5チェックサムをアサートするテストを作成することをお勧めします。

48
Szczepan

これについてあまり心配する必要はないと思います。 EasymockとMockitoはどちらも「厳格」または「ニース」に設定できます。唯一の違いは、デフォルトではEasymockが厳格であるのに対し、Mockitoはナイスです。

すべてのテストと同様に、厳密な規則はありません。テストの信頼性と保守性のバランスをとる必要があります。私は通常、「厳格な」モックを使用するために高いレベルの信頼を必要とする特定の機能的または技術的な領域があることに気づきます。たとえば、debitAccount()メソッドを2回以上呼び出さないようにすることもできます。ただし、モックが実際にはスタブに過ぎない場合もあるので、コードの実際の「肉」をテストできます。

Mockitoの初期の頃はAPIの互換性が問題でしたが、現在はより多くのツールがフレームワークをサポートしています。 Powermock(個人的なお気に入り)にmockito拡張機能が追加されました

7
user404345

私は正直に言うとmockitoを好みます。 EasyMockをユニティルで使用していて、両方を組み合わせると、IllegalArgumentExceptionなどの例外が発生することがよくあります。MissingBehaviorExceptionsだけでなく、インターフェースでもありません。どちらの場合でも、コードとテストコードは完全に問題ありません。 MissingBehaviorExceptionは、createMock(classextentions !!を使用)で作成されたモックオブジェクトがこのエラーを生成したためであるように見えました。 @Mockを使用するとうまくいきました!私はそのような誤解を招くような振る舞いは好きではありません。私にとってそれは開発者が何をしているのか知らないことを明確に示しています。優れたフレームワークは常に使いやすく、あいまいでない必要があります。 IllegalArgumentExceptionは、EasyMock内部のいくつかの混同が原因でした。また、レコーディングは私がやりたいことではありません。私のコードが例外をスローするかどうか、そして期待される結果を返すかどうかをテストしたいと思います。コードカバレッジと組み合わせることは、私にとって適切なツールです。パフォーマンスが向上するため、前のコードの1行上または下にコードを1行挿入しても、テストが中断しないようにします。 mockitoでは問題ありません。 EasyMockを使用すると、コードが壊れていなくてもテストが失敗します。それは悪いです。時間とお金がかかります。予想される動作をテストしたい。本当に順番を気にしていますか?まれにあなたがそうするかもしれません。次にEasymockを使用します。それ以外の場合は、mockitoを使用してテストを作成する時間を大幅に短縮できると思います。

親切にローレンス

5
Lawrence