mocking private static
例を使用して、Mockitoでプライベートメソッド、静的メソッド、およびボイドメソッドをモックする方法を学びます。
この一連のハンズオンでは Mockitoのチュートリアル 、私たちは見ていた さまざまな種類のMockitoマッチャー 前回のチュートリアルで。
一般的に言えば、プライベートメソッドと静的メソッドのモックは、異常なモックのカテゴリに分類されます。
プライベートおよび静的メソッド/クラスをモックする必要が生じた場合、それはリファクタリングが不十分なコードを示し、実際にはテスト可能なコードではなく、使用されなかった一部のレガシーコードはユニットテストに非常に適している可能性があります。
そうは言っても、PowerMockitoのようないくつかのユニットテストフレームワークによる(Mockitoによる直接ではなく)プライベートメソッドと静的メソッドのモックのサポートはまだ存在します。
データベース行の更新など、本質的に何も返さないメソッドがある可能性があるため、「void」メソッドのモックが一般的です(入力を受け入れ、出力を返さないRest APIエンドポイントのPUT操作と見なしてください)。
Mockitoは、ボイドメソッドのモックを完全にサポートしています。これについては、この記事の例で説明します。
SQLテストの質問と回答pdf
学習内容:
パワーモック –簡単な紹介
Mockitoの場合、プライベートメソッドと静的メソッドをモックするための直接サポートはありません。プライベートメソッドをテストするには、次のことを行う必要があります コードをリファクタリングする 保護された(またはパッケージ)へのアクセスを変更するには、静的/最終メソッドを回避する必要があります。
私の意見では、Mockitoはこれらの種類のモックを意図的にサポートしていません。これらの種類のコード構造を使用すると、コードの臭いや設計が不十分なコードになるためです。
ただし、プライベートメソッドと静的メソッドのモックをサポートするフレームワークがあります。
パワーモック EasyMockやMockitoなどの他のフレームワークの機能を拡張し、静的メソッドとプライベートメソッドをモックする機能を提供します。
#1)方法: Powermockは、プライベートメソッドと静的メソッド、最終クラス、コンストラクターなどのモックをサポートするために、カスタムバイトコード操作の助けを借りてこれを行います。
#2)サポートされているパッケージ: Powermockは、Mockito用とeasyMock用の2つの拡張APIを提供します。この記事のために、パワーモック用のMockito拡張機能を使用した例を記述します。
#3)構文 :Powermockitoの構文は、静的メソッドとプライベートメソッドをモックするためのいくつかの追加メソッドを除いて、Mockitoとほぼ同じです。
#4)Powermockitoのセットアップ
GradleベースのプロジェクトにMockitoライブラリを含めるために、含めるライブラリは次のとおりです。
testCompile group: 'org.powermock', name: 'powermock-api-mockito2', version: '1.7.4' testCompile group: 'org.powermock', name: 'powermock-module-junit4', version: '1.7.4'
同様の依存関係がMavenでも利用できます。
Powermock-api-mockito2 –ライブラリには、PowermockitoのMockito拡張機能を含める必要があります。
Powermock-module-junit4 –モジュールには、PowerMockRunner(PowerMockitoでテストを実行するために使用されるカスタムランナー)を含める必要があります。
ここで注意すべき重要な点は、PowerMockはJunit5テストランナーをサポートしていないということです。したがって、テストはJunit4に対して作成する必要があり、テストはPowerMockRunnerを使用して実行する必要があります。
PowerMockRunnerを使用するには–テストクラスに注釈を付ける必要があります @RunWith(PowerMockRunner.class)
それでは、プライベートメソッド、静的メソッド、およびvoidメソッドを詳細にモックして説明しましょう。
プライベートメソッドをあざける
テスト対象のメソッドから内部的に呼び出されるプライベートメソッドのモックは、特定の時間に避けられない場合があります。 powermockitoを使用すると、これが可能になり、「verifyPrivate」という名前の新しいメソッドを使用して検証が行われます。
取りましょう AN例 ここで、テスト対象のメソッドはプライベートメソッド(ブール値を返す)を呼び出します。テストに応じてこのメソッドをスタブしてtrue / falseを返すには、このクラスにスタブを設定する必要があります。
この例では、テスト対象のクラスは、いくつかのインターフェイス呼び出しとプライベートメソッド呼び出しをモックするスパイインスタンスとして作成されています。
モックプライベートメソッドの重要なポイント:
#1) テストメソッドまたはテストクラスには@のアノテーションを付ける必要があります PrepareForTest (ClassUnderTest)。このアノテーションは、powerMockitoにテスト用に特定のクラスを準備するように指示します。
これらは主に必要なクラスになります 操作されたバイトコード 。通常、最終クラスの場合、テスト中にモックする必要があるプライベートメソッドや静的メソッドを含むクラス。
例:
@PrepareForTest(PriceCalculator.class)
#二) プライベートメソッドでスタブを設定します。
構文 - when(モックまたはスパイインスタンス、「privateMethodName」)。thenReturn(//戻り値)
例:
when (priceCalculatorSpy, 'isCustomerAnonymous').thenReturn(false);
#3) スタブされたプライベートメソッドを検証します。
構文 – verifyPrivate(mockedInstance).invoke( 'privateMethodName')
例:
verifyPrivate (priceCalculator).invoke('isCustomerAnonymous');
完全なテストサンプル: 以前の記事と同じ例を続けます。ここでは、priceCalculatorにitemService、userServiceなどのモックされた依存関係があります。
同じクラス内のプライベートメソッドを呼び出し、顧客が匿名であるかどうかを返す、calculatePriceWithPrivateMethodという新しいメソッドを作成しました。
@Test @PrepareForTest(PriceCalculator.class) public void calculatePriceForAnonymous_witStubbedPrivateMethod_returnsCorrectPrice() throws Exception { // Arrange ItemSku item1 = new ItemSku(); item1.setApplicableDiscount(5.00); item1.setPrice(100.00); double expectedPrice = 90.00; // Setting up stubbed responses using mocks when(priceCalculatorSpy, 'isCustomerAnonymous').thenReturn(false); when(mockedItemService.getItemDetails(123)).thenReturn(item1); // Act double actualDiscountedPrice = priceCalculatorSpy.calculatePriceWithPrivateMethod(123); // Assert verifyPrivate(priceCalculator).invoke('isCustomerAnonymous'); assertEquals(expectedPrice, actualDiscountedPrice); }
静的メソッドのモック
静的メソッドは、プライベートメソッドで見たのと同様の方法でモックすることができます。
テスト対象のメソッドが同じクラス(または別のクラス)の静的メソッドを使用する場合、テストの前(またはテストクラス)にそのクラスをprepareForTestアノテーションに含める必要があります。
モック静的メソッドの重要なポイント:
#1) テストメソッドまたはテストクラスには@のアノテーションを付ける必要があります PrepareForTest (ClassUnderTest)。プライベートメソッド/クラスのモックと同様に、これは静的クラスにも必要です。
#二) 静的メソッドに必要な追加の手順は次のとおりです– mockStatic(//静的クラスの名前)
例:
mockStatic(DiscountCategoryFinder.class)
#3) 静的メソッドでスタブを設定するには、他のインターフェイス/クラスのモックインスタンスでメソッドをスタブするのと同じくらい優れています。
例えば: DiscountCategoryFinderクラスの静的メソッドgetDiscountCategory()(値がPREMIUM&GENERALの列挙型DiscountCategoryを返す)をスタブするには、次のようにスタブします。
when (DiscountCategoryFinder. getDiscountCategory ()).thenReturn(DiscountCategory. PREMIUM );
#4) 最終/静的メソッドのモック設定を検証するには、verifyStatic()メソッドを使用できます。
例:
verifyStatic (DiscountCategoryFinder.class, times (1));
ボイドメソッドのモック
まず、voidメソッドのスタブが含まれる可能性のあるユースケースの種類を理解してみましょう。
#1) たとえば、メソッド呼び出し–プロセス中に電子メール通知を送信します。
例えば :インターネットバンキングアカウントのパスワードを変更するとします。変更が成功すると、電子メールで通知が届きます。
これは、顧客に電子メール通知を送信するためのvoidメソッド呼び出しを含むBankAPIへのPOST呼び出しとしての/ changePasswordと考えることができます。
#二) voidメソッド呼び出しのもう1つの一般的な例は、DBへの更新された要求であり、入力を受け取り、何も返しません。
voidメソッド(つまり、何も返さない、または例外をスローするメソッド)のスタブは、次を使用して処理できます。 doNothing()、doThrow()およびdoAnswer()、doCallRealMethod()関数 。テストの予想に従って、上記の方法を使用してスタブを設定する必要があります。
また、すべてのvoidメソッド呼び出しはデフォルトでdoNothing()にモックされていることに注意してください。したがって、明示的なモックセットアップが行われていなくても 無効 メソッド呼び出しの場合、デフォルトの動作は引き続きdoNothing()です。
これらすべての関数の例を見てみましょう。
すべての例で、クラスがあると仮定しましょう StudentScoreUpdates 方法があります computeSumAndStore()。 このメソッドは、スコアの合計を(入力として)計算し、 ボイド 方法 updateScores() databaseImplementationインスタンス上。
public class StudentScoreUpdates { public IDatabase databaseImpl; public StudentScoreUpdates(IDatabase databaseImpl) { this.databaseImpl = databaseImpl; } public void calculateSumAndStore(String studentId, int() scores) { int total = 0; for(int score : scores) { total = total + score; } // write total to DB databaseImpl.updateScores(studentId, total); } }
以下の例を使用して、モックメソッド呼び出しの単体テストを作成します。
#1)doNothing() – doNothing()は、Mockitoのvoidメソッド呼び出しのデフォルトの動作です。つまり、voidメソッドの呼び出しを検証した場合でも(doNothing()にvoidを明示的に設定しなくても、検証は成功します)
public void calculateSumAndStore_withValidInput_shouldCalculateAndUpdateResultInDb() { // Arrange studentScores = new StudentScoreUpdates(mockDatabase); int() scores = {60,70,90}; Mockito.doNothing().when(mockDatabase).updateScores(anyString(), anyInt()); // Act studentScores.calculateSumAndStore('student1', scores); // Assert Mockito.verify(mockDatabase, Mockito.times(1)).updateScores(anyString(), anyInt()); }
doNothing()と一緒の他の使用法
に) voidメソッドが複数回呼び出され、最初の呼び出しに対して– doNothing()のように、呼び出しごとに異なる応答を設定し、次の呼び出しで例外をスローする場合。
例えば :次のようにモックを設定します。
Mockito. doNothing ().doThrow(new RuntimeException()).when(mockDatabase).updateScores( anyString (), anyInt ());
b) voidメソッドが呼び出された引数をキャプチャする場合は、MockitoのArgumentCaptor機能を使用する必要があります。これにより、メソッドが呼び出された引数の検証が追加されます。
ArgumentCaptorの例:
public void calculateSumAndStore_withValidInput_shouldCalculateAndUpdateResultInDb() { // Arrange studentScores = new StudentScoreUpdates(mockDatabase); int() scores = {60,70,90}; Mockito.doNothing().when(mockDatabase).updateScores(anyString(), anyInt()); ArgumentCaptor studentIdArgument = ArgumentCaptor.forClass(String.class); // Act studentScores.calculateSumAndStore('Student1', scores); // Assert Mockito.verify(mockDatabase, Mockito.times(1)).updateScores(studentIdArgument.capture(), anyInt()); assertEquals('Student1', studentIdArgument.getValue()); }
#2)doThrow()- これは、テスト対象のメソッドからvoidメソッドが呼び出されたときに単に例外をスローしたい場合に役立ちます。
例えば:
Mockito.doThrow(newRuntimeException()).when(mockDatabase).updateScores ( anyString (), anyInt ());
#3)doAnswer()- doAnswer()は、カスタムロジックを実行するためのインターフェイスを提供するだけです。
例えば。 渡された引数を介して一部の値を変更し、通常のスタブでは特にvoidメソッドでは返せなかったカスタム値/データを返します。
デモンストレーションの目的で– updateScores()voidメソッドをスタブして「 回答() 」を入力し、メソッドが呼び出されるべきときに渡されるべき引数の1つの値を出力します。
コード例:
@Test public void calculateSumAndStore_withValidInput_shouldCalculateAndUpdateResultInDb() { // Arrange studentScores = new StudentScoreUpdates(mockDatabaseImpl); int() scores = {60,70,90}; Mockito.doCallRealMethod().when(mockDatabaseImpl).updateScores(anyString(), anyInt()); doAnswer(invocation -> { Object() args = invocation.getArguments(); Object mock = invocation.getMock(); System.out.println(args(0)); return mock; }).when(mockDatabaseImpl).updateScores(anyString(), anyInt()); // Act studentScores.calculateSumAndStore('Student1', scores); // Assert Mockito.verify(mockDatabaseImpl, Mockito.times(1)).updateScores(anyString(), anyInt()); }
#4)doCallRealMethod()- 部分的なモックはスタブに似ています(一部のメソッドに対して実際のメソッドを呼び出し、残りをスタブアウトすることができます)。
voidメソッドの場合、mockitoは、モックをセットアップしようとしているときに使用できるdoCallRealMethod()と呼ばれる特別な関数を提供します。これが行うことは、実際の引数を使用して実際のvoidメソッドを呼び出すことです。
例えば:
任意のウェブサイトからビデオをダウンロードするためのソフトウェア
Mockito. doCallRealMethod ().when(mockDatabaseImpl).updateScores( anyString (), anyInt ());
ヒントとコツ
#1)同じテストメソッド/クラスに複数の静的クラスを含める–PowerMockitoの使用 Finalクラスの複数のStaticをモックする必要がある場合は、@のクラス名 PrepareForTest アノテーションは、配列としてのコンマ区切り値として言及できます(基本的にクラス名の配列を受け入れます)。
例:
@PrepareForTest({PriceCalculator.class, DiscountCategoryFinder.class})
上記の例に示されているように、PriceCalculatorとDiscountCategoryFinderの両方がモックする必要のある最終クラスであると想定します。これらは両方とも、PrepareForTestアノテーションのクラスの配列として言及でき、テストメソッドでスタブ化できます。
#2)PrepareForTest属性の配置– この属性の位置付けは、Testクラスに含まれるテストの種類に関して重要です。
すべてのテストで同じ最終クラスを使用する必要がある場合は、テストクラスレベルでこの属性に言及するのが理にかなっています。これは、準備されたクラスがすべてのテストメソッドで使用できることを意味します。これとは対照的に、注釈がテストメソッドで言及されている場合、それはその特定のテストでのみ使用可能になります
結論
このチュートリアルでは、静的メソッド、finalメソッド、およびvoidメソッドをモックするさまざまなアプローチについて説明しました。
多くの静的メソッドまたは最終メソッドを使用するとテスト容易性が妨げられますが、それでも、一般的に使用されていないレガシーコードでもコード/アプリケーションの信頼性を高めるために、単体テストの作成を支援するテスト/モックのサポートが利用可能です。テスト容易性のために設計されています。
静的メソッドとfinalメソッドの場合、Mockitoにはすぐに使用できるサポートはありませんが、PowerMockito(Mockitoから多くのものを大幅に継承)などのライブラリはそのようなサポートを提供し、これらの機能をサポートするために実際にバイトコード操作を実行する必要があります。
Mockitoは、すぐに使用できるvoidメソッドのスタブをサポートし、doNothing、doAnswer、doThrow、doCallRealMethodなどのさまざまなメソッドを提供し、テストの要件に従って使用できます。
最もよくあるMockitoインタビューの質問は、次のチュートリアルで簡単に説明されています。
推奨読書
- Mockitoチュートリアル:ユニットテストでモックするためのMockitoフレームワーク
- Mockitoインタビューの質問トップ12(Mocking Frameworkインタビュー)
- C ++では静的
- メソッドとライフサイクルを備えたJavaスレッド
- コード例を使用してMockitoでモックとスパイを作成する
- Mockitoが提供するさまざまなタイプのマッチャー
- 欠陥防止の方法と技術
- 一括テスト実行のためにSoapUIのメソッドを使用する方法-SoapUIチュートリアル#10