更新2019年9月:唯一の春ブーツで(デフォルトで)サポートモックフレームワークがある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サブクラスの生成を通じて、クラスのモックオブジェクトの作成をサポートします。そのため、上記のクラスはfinalにすることはできず、オーバーライド可能なインスタンスメソッドのみをモックできます。ただし、最も重要なのは、これらのツールを使用する場合、テスト対象のコードの依存関係(つまり、テスト対象の特定のクラスが依存する他のクラスのオブジェクト)をテストによって制御して、モックインスタンスをクライアントに渡すことができるようにすることです。それらの依存関係の。したがって、単体テストを記述したいクライアントクラスの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(期待)は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に渡す必要がありますが、より単純で安全です。JMockitはAttach APIを使用してオンデマンドでJavaエージェントを透過的にロードできるため、JDK 1.6+(古いバージョンにデプロイする場合でも、常に開発に使用できます)ではこのような要件はありません。
最近のもう1つのモッキングツールはMockitoです。古いツール(jMock、EasyMock)の制限を克服しようとはしていませんが、モックを使った新しいスタイルの動作テストを導入しています。JMockitは、Verifications APIを通じてこの代替スタイルもサポートしています。
JMockit対Mockito
- Mockitoは、APIへの明示的な呼び出しに依存して、レコード(when(...))フェーズと検証(verify(...))フェーズの間でコードを分離します。つまり、テストコードでモックオブジェクトを呼び出す場合も、モック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(); result = new RuntimeException();。
- さらに別の矛盾は、Mockitoスパイの使用で発生します。スパイされたインスタンスで実際のメソッドを実行できるようにする「モック」。たとえば、spyが空のリストを参照している場合、when(spy.get(0))。thenReturn( "foo")を記述する代わりに、doReturn( "foo")。when(spy).get()を記述する必要があります。 0)。JMockitでは、動的モッキング機能はスパイと同様の機能を提供しますが、実際のメソッドは再生フェーズでのみ実行されるため、この問題はありません。
- EasyMockおよびjMock(Javaの最初のモックAPI)では、(デフォルトで)予期しない呼び出しを許可しないモックオブジェクトに対して、モックメソッドの予想される呼び出しの記録に完全に重点が置かれていました。これらのAPIは、予期しない呼び出しを許可するモックオブジェクトの許可された呼び出しの記録も提供しますが、これはセカンドクラスの機能として扱われました。さらに、これらのツールでは、テスト対象のコードの実行後にモックへの呼び出しを明示的に確認する方法はありません。このような検証はすべて暗黙的かつ自動的に実行されます。
- Mockito(およびUnitils Mock)では、反対の視点が取られます。記録されているかどうかにかかわらず、テスト中に発生する可能性のあるモックオブジェクトへのすべての呼び出しは許可されますが、決して期待されません。検証は、テスト中のコードの実行後に明示的に実行されますが、自動的には実行されません。
- どちらのアプローチも極端すぎるため、最適ではありません。JMockit Expectations&Verificationsは、開発者が各テストの厳密な(デフォルトで期待される)および非厳密な(デフォルトで許可される)モック呼び出しの最適な組み合わせをシームレスに選択できる唯一のAPIです。
- より明確にするために、Mockito APIには次の欠点があります。テスト中にvoid以外のモックメソッドへの呼び出しが発生したことを確認する必要があるが、テストが戻り値の型のデフォルトとは異なるそのメソッドからの戻り値を必要とする場合、Mockitoテストには重複したコードが含まれます。レコードフェーズではwhen(mock.someMethod())。thenReturn(xyz)を呼び出し、検証フェーズではverify(mock).someMethod()を呼び出します。JMockitを使用すると、常に厳密な期待を記録できます。これは明示的に検証する必要はありません。または、記録された厳密でない期待値に対して呼び出し回数の制約(時間= 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テストツールキットにはあり、より広い範囲と、より野心的な目標を完全かつ洗練された開発者のテスト・ソリューションを提供するために、他のモックツールキットよりを。モック用の優れたAPIは、人為的な制限がなくても、テストを生産的に作成するには不十分です。IDEに依存せず、使いやすく、よく統合されたコードカバレッジツールも不可欠です。これが、JMockitカバレッジが提供することを目指しています。テストスイートのサイズが大きくなるにつれて、さらに役立つ開発者テストツールセットのもう1つの部分は、本番用コードへのローカライズされた変更後にテストを段階的に再実行する機能です。これは、カバレッジツールにも含まれています。
(確かに、ソースには偏りがあるかもしれませんが、まあ...)
私はJMockitで行くと思います。これは最も使いやすく、柔軟で、テストするクラスを制御できない(または互換性の理由などでクラスを壊すことができない)場合でも、ほとんどすべての場合に機能し、困難なケースやシナリオでも機能します。
JMockitでの私の経験は非常に前向きです。