プロジェクトに使用するモックフレームワークを調査しており、JMockitとMockitoに絞り込んでいます。
MockitoがStackoverflowで「 Javaに最適なモックフレームワーク」に選ばれたことに気付きました。JMockitの「モックツール比較マトリックス」の
機能を比較すると、 JMockitには複数の異なる機能があるように見えます。
JMockitでは達成できないMockitoでできること、またはその逆 について、誰かが特定の情報(意見ではない)を持っていますか?
プロジェクトに使用するモックフレームワークを調査しており、JMockitとMockitoに絞り込んでいます。
MockitoがStackoverflowで「 Javaに最適なモックフレームワーク」に選ばれたことに気付きました。JMockitの「モックツール比較マトリックス」の
機能を比較すると、 JMockitには複数の異なる機能があるように見えます。
JMockitでは達成できないMockitoでできること、またはその逆 について、誰かが特定の情報(意見ではない)を持っていますか?
2019年9月の更新:Spring Bootで(デフォルトで)サポートされている唯一のモックフレームワークはMockitoです。Springを使用する場合、答えは非常に明白です。
競争はJMockitとPowerMock、そしてMockitoの間だと思います。
「プレーン」なjMockとEasyMockは、プロキシとCGLIBのみを使用し、新しいフレームワークのようにJava 5インストルメンテーションを使用しないため、そのままにしておきます。
jMockも4年以上安定したリリースがありませんでした。jMock 2.6.0は、RC1からRC2に移行するのに2年かかり、実際にリリースされるまでにさらに2年かかりました。
プロキシとCGLIBとインストルメンテーションについて:
(EasyMockおよびjMock)はjava.lang.reflect.Proxyに基づいており、インターフェースを実装する必要があります。さらに、CGLIBサブクラスの生成を通じて、クラスのモックオブジェクトの作成をサポートします。そのため、上記のクラスを最終的にすることはできず、オーバーライド可能なインスタンスメソッドのみをモックすることができます。ただし、最も重要なのは、これらのツールを使用する場合、テスト対象のコードの依存関係(つまり、テスト対象の特定のクラスが依存する他のクラスのオブジェクト)をテストによって制御して、モックインスタンスをクライアントに渡すことができるようにする必要があることです。それらの依存関係の。したがって、依存関係は、単体テストを作成するクライアントクラスのnew演算子を使用して単純にインスタンス化することはできません。
最終的に、従来のモックツールの技術的な制限により、製品コードに次の設計上の制限が課せられます。
- テストでモックする必要がある可能性のある各クラスは、個別のインターフェースを実装するか、最終的なものではない必要があります。
- テストする各クラスの依存関係は、構成可能なインスタンス作成メソッド(ファクトリまたはサービスロケーター)を介して取得するか、依存性注入のために公開する必要があります。そうしないと、単体テストは、依存関係のモック実装をテスト対象の単体に渡すことができません。
- モックできるのはインスタンスメソッドのみであるため、単体テスト対象のクラスは、依存関係で静的メソッドを呼び出すことも、コンストラクターを使用してインスタンス化することもできません。
上記はhttp://jmockit.org/about.htmlからコピーされています。さらに、それ自体(JMockit)、PowerMock、およびMockitoをいくつかの方法で比較します。
PowerMock、jEasyTest、MockInjectなど、従来のツールの制限を克服するJava用のモックツールが他にもあります。JMockitの機能セットに最も近いのはPowerMockなので、ここで簡単に評価します(他の2つはさらに制限されており、現在積極的に開発されていないようです)。
JMockitとPowerMock
- まず第一に、PowerMockはモック用の完全なAPIを提供しませんが、代わりに別のツール(現在はEasyMockまたはMockito)の拡張として機能します。これは、これらのツールの既存のユーザーにとって明らかに利点です。
- 一方、JMockitはまったく新しいAPIを提供しますが、そのメインAPI(Expectations)はEasyMockとjMockの両方に似ています。これにより、学習曲線が長くなりますが、JMockitは、よりシンプルで一貫性があり、使いやすいAPIを提供することもできます。
- JMockit Expectations APIと比較すると、PowerMock APIはより「低レベル」であり、ユーザーはテスト用に準備する必要のあるクラスを把握して指定する必要があります(@PrepareForTest({ClassA.class、...})アノテーションを使用) )および本番コードに存在する可能性のあるさまざまな種類の言語構造を処理するために特定のAPI呼び出しを要求する:静的メソッド(mockStatic(ClassA.class))、コンストラクター(suppress(constructor(ClassXyz.class)))、コンストラクター呼び出し( expectNew(AClass.class))、部分モック(createPartialMock(ClassX.class、 "methodToMock"))など。
- JMockit Expectationsを使用すると、すべての種類のメソッドとコンストラクターが純粋に宣言的な方法でモックされ、@ Mockedアノテーションの正規表現を介して、または期待値が記録されていないメンバーを単に「モック解除」することで、部分的なモックが指定されます。つまり、開発者は、テストクラスの共有「モックフィールド」、または個々のテストメソッドの「ローカルモックフィールド」や「モックパラメータ」を宣言するだけです(この最後の場合、@Mockedアノテーションは宣言しないことがよくあります)。必要とされる)。
- EqualsやhashCodeのモックのサポート、オーバーライドされたメソッドなど、JMockitで使用可能な一部の機能は、現在PowerMockではサポートされていません。また、テストコード自体が実際の実装クラスの知識を持たない限り、テストの実行時に指定された基本タイプのインスタンスとモック実装をキャプチャするJMockitの機能に相当するものはありません。
- PowerMockは、モックされたクラスの変更されたバージョンを生成するために、カスタムクラスローダー(通常はテストクラスごとに1つ)を使用します。カスタムクラスローダーをこのように頻繁に使用すると、サードパーティライブラリとの競合が発生する可能性があるため、テストクラスで@PowerMockIgnore( "package.to.be.ignored")アノテーションを使用する必要がある場合があります。
- JMockit(「Javaエージェント」を介したランタイムインストルメンテーション)で使用されるメカニズムは、JDK 1.5で開発するときに「-javaagent」パラメーターをJVMに渡す必要がありますが、よりシンプルで安全です。JDK 1.6以降(古いバージョンにデプロイする場合でも常に開発に使用できます)では、JMockitはAttach APIを使用してJavaエージェントをオンデマンドで透過的にロードできるため、このような要件はありません。
もう1つの最近のモックツールはMockitoです。古いツール(jMock、EasyMock)の制限を克服しようとはしていませんが、モックを使用した新しいスタイルの動作テストを導入しています。JMockitは、VerificationsAPIを介してこの代替スタイルもサポートしています。
JMockit vs Mockito
- Mockitoは、レコード(when(...))フェーズと検証(verify(...))フェーズの間でコードを分離するために、APIへの明示的な呼び出しに依存しています。これは、テストコードでモックオブジェクトを呼び出す場合は、モックAPIを呼び出す必要があることを意味します。さらに、これにより、when(...)およびverify(mock)...の呼び出しが繰り返されることがよくあります。
- JMockitでは、同様の呼び出しは存在しません。もちろん、新しいNonStrictExpectations()および新しいVerifications()コンストラクター呼び出しがありますが、これらはテストごとに1回だけ発生し(通常)、モックされたメソッドおよびコンストラクターの呼び出しとは完全に分離されています。
- Mockito APIには、モックされたメソッドの呼び出しに使用される構文にいくつかの矛盾が含まれています。記録フェーズでは、when(mock.mockedMethod(args))...のような呼び出しがありますが、検証フェーズでは、この同じ呼び出しがverify(mock).mockedMethod(args)として書き込まれます。最初のケースでは、mockedMethodの呼び出しはモックオブジェクトで直接行われ、2番目のケースでは、verify(mock)によって返されたオブジェクトで行われることに注意してください。
- モックされたメソッドの呼び出しは常にモックされたインスタンス自体で直接行われるため、JMockitにはそのような矛盾はありません。(1つの例外を除いて、同じモックインスタンスでの呼び出しを照合するために、onInstance(mock)呼び出しが使用され、onInstance(mock).mockedMethod(args)のようなコードになります。ただし、ほとんどのテストではこれを使用する必要はありません。 )。
- メソッドのチェーン/ラッピングに依存する他のモックツールと同様に、Mockitoもvoidメソッドをスタブするときに一貫性のない構文に遭遇します。たとえば、when(mockedList.get(1))。thenThrow(new RuntimeException());と記述します。非voidメソッドの場合、およびdoThrow(new RuntimeException())。when(mockedList).clear(); 無効なもののために。JMockitでは、常に同じ構文です。mockedList.clear(); 結果=newRuntimeException();。
- さらに別の矛盾は、Mockitoスパイの使用で発生します。それは、スパイされたインスタンスで実際のメソッドを実行できるようにする「モック」です。たとえば、spyが空のリストを参照している場合、when(spy.get(0))。thenReturn( "foo")を記述する代わりに、doReturn( "foo")。when(spy).get( 0)。JMockitを使用すると、動的モック機能はスパイと同様の機能を提供しますが、実際のメソッドは再生フェーズでのみ実行されるため、この問題は発生しません。
- Java用の最初のモックAPIであるEasyMockとjMockでは、(デフォルトで)予期しない呼び出しを許可しないモックオブジェクトに対して、モックされたメソッドの予想される呼び出しの記録に完全に焦点が当てられていました。これらのAPIは、予期しない呼び出しを許可するモックオブジェクトの許可された呼び出しの記録も提供しますが、これは第2クラスの機能として扱われました。さらに、これらのツールでは、テスト対象のコードが実行された後、モックへの呼び出しを明示的に検証する方法はありません。このような検証はすべて、暗黙的かつ自動的に実行されます。
- Mockito(およびUnitils Mock)では、反対の視点が取られます。記録されているかどうかに関係なく、テスト中に発生する可能性のあるモックオブジェクトへのすべての呼び出しは許可されますが、予期されることはありません。検証は、テスト対象のコードが実行された後に明示的に実行され、自動的には実行されません。
- どちらのアプローチも極端すぎるため、最適とは言えません。JMockit Expectations&Verificationsは、開発者が各テストに対して厳密(デフォルトで期待される)と非厳密(デフォルトで許可される)の模擬呼び出しの最適な組み合わせをシームレスに選択できるようにする唯一のAPIです。
- より明確にするために、MockitoAPIには次の欠点があります。テスト中に非voidモックメソッドの呼び出しが発生したことを確認する必要があるが、テストでそのメソッドからの戻り値が戻り型のデフォルトとは異なる場合、Mockitoテストのコードは重複します。記録フェーズではwhen(mock.someMethod())。thenReturn(xyz)を呼び出し、検証フェーズではverify(mock).someMethod()を呼び出します。JMockitを使用すると、厳密な期待値を常に記録でき、明示的に検証する必要はありません。または、記録された非厳密な期待値に対して呼び出しカウント制約(times = 1)を指定できます(Mockitoでは、このような制約はverify(mock、constraint)呼び出しでのみ指定できます)。
- Mockitoの構文は、順番に検証したり、完全に検証したりする(つまり、モックオブジェクトへのすべての呼び出しが明示的に検証されていることを確認する)ための構文が不十分です。最初のケースでは、追加のオブジェクトを作成する必要があり、そのオブジェクトに対して行われた検証を呼び出す必要があります:InOrder inOrder = inOrder(mock1、mock2、...)。2番目のケースでは、verifyNoMoreInteractions(mock)やverifyZeroInteractions(mock1、mock2)などの呼び出しを行う必要があります。
- JMockitを使用すると、新しいVerifications()(または両方の要件を組み合わせるための新しいFullVerificationsInOrder())の代わりに、新しいVerificationsInOrder()または新しいFullVerifications()を記述するだけです。関連するモックオブジェクトを指定する必要はありません。余分なモックAPI呼び出しはありません。また、ボーナスとして、順序付けられた検証ブロック内でunverifiedInvocations()を呼び出すことにより、Mockitoでは単純に不可能な順序関連の検証を実行できます。
最後に、JMockit Testing Toolkitは、完全で洗練された開発者テストソリューションを提供するために、他のモックツールキットよりも広い範囲と野心的な目標を持っています。人工的な制限がなくても、モック用の優れたAPIは、テストを生産的に作成するには十分ではありません。IDEにとらわれず、使いやすく、十分に統合されたコードカバレッジツールも不可欠であり、それがJMockitCoverageが提供することを目的としています。テストスイートのサイズが大きくなるにつれてさらに役立つ開発者テストツールセットのもう1つの部分は、本番コードにローカライズされた変更を行った後、テストを段階的に再実行する機能です。これはカバレッジツールにも含まれています。
(もちろん、ソースにバイアスがかかっている可能性がありますが、まあ...)
JMockitで行くと思います。これは最も使いやすく、柔軟性があり、テストするクラスを制御できない(または互換性の理由などで中断できない)場合でも、ほとんどすべての場合に機能します。
JMockitでの私の経験は非常に前向きでした。
私は Mockito と JMockit の両方を使用しており、それらの経験は次のとおりです。
モッキート:
イージーモック:
JMockit:
さらに、JMockit のその他の利点:
個人的には JMockit の方が機能が豊富で柔軟性が高いと思いますが、少し急な学習曲線が必要です。通常、同じモック効果を実現するには複数の方法があり、モックを設計するときはより注意が必要です。
私が jMockit を使用するのは、それが Deencapsultation.class のリフレクション ライブラリだからです。私は実際に Mockito のスタイルが大好きですが、限られたテスト フレームワークがそれに到達できるように、コードを変更したり、API を混乱させたりすることは拒否しています。そして、私はすべてのコードをテストするのが好きなので、プライベート メソッドを簡単にテストできないフレームワークは、私が使いたいものではありません。
この記事に心動かされた
(確かに大きな) 学習曲線の後、jMockit は現在、モックのメインのユニット テスト フレームワークです。
従来のコードベース (多数の静的メソッド呼び出しなど) を簡単にテストするには、JMockit が非常に役立ちます。【ブログ記事のハレンチプラグ】
私は個人的にEasyMockが好きです。
素敵な、通常の、そして厳密なモックコントロールを切り替える機能は、私のお気に入りの機能の1つです。