web-dev-qa-db-ja.com

モッキート-スパイvsモック

Mockito-スパイはオブジェクトの実際のメソッドを呼び出し、モックはdoubleオブジェクトのメソッドを呼び出します。また、コードの匂いがない限り、スパイは避けるべきです。ただし、スパイはどのように機能し、実際にいつ使用する必要がありますか?モックとはどう違うのですか?

70
Abhinav

技術的に言えば、「モック」と「スパイ」の両方は、特別な種類の「テストダブル」です。

モッキートは残念ながら区別を奇妙にしています。

mockitoのモックは通常のモックです他のモックフレームワークでは(呼び出しをスタブできます。つまり、メソッド呼び出しから特定の値を返します)。

mockitoのスパイは部分的な模擬です他の模擬フレームワークでは(オブジェクトの一部は模擬され、一部は実際のメソッド呼び出しを使用します)。

67

両方を使用して、メソッドまたはフィールドをモックできます。違いは、モックでは完全なモックまたは偽のオブジェクトを作成し、スパイでは実際のオブジェクトがあり、その特定のメソッドをスパイまたはスタブするだけであることです。

もちろん、スパイオブジェクトでは実際のメソッドであるため、メソッドをスタブ化していない場合は、実際のメソッドの動作を呼び出します。メソッドを変更してモックする場合は、スタブする必要があります。

比較として以下の例を考えてください。

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.runners.MockitoJUnitRunner;
 
import Java.util.ArrayList;
import Java.util.List;
 
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;
 
@RunWith(MockitoJUnitRunner.class)
public class MockSpy {
 
    @Mock
    private List<String> mockList;
 
    @Spy
    private List<String> spyList = new ArrayList();
 
    @Test
    public void testMockList() {
        //by default, calling the methods of mock object will do nothing
        mockList.add("test");

        Mockito.verify(mockList).add("test");
        assertEquals(0, mockList.size());
        assertNull(mockList.get(0));
    }
 
    @Test
    public void testSpyList() {
        //spy object will call the real method when not stub
        spyList.add("test");

        Mockito.verify(spyList).add("test");
        assertEquals(1, spyList.size());
        assertEquals("test", spyList.get(0));
    }
 
    @Test
    public void testMockWithStub() {
        //try stubbing a method
        String expected = "Mock 100";
        when(mockList.get(100)).thenReturn(expected);
 
        assertEquals(expected, mockList.get(100));
    }
 
    @Test
    public void testSpyWithStub() {
        //stubbing a spy method will result the same as the mock object
        String expected = "Spy 100";
        //take note of using doReturn instead of when
        doReturn(expected).when(spyList).get(100);
 
        assertEquals(expected, spyList.get(100));
    }
}

シャウドするとき、モックまたはスパイを使用しますか?安全を確保し、外部サービスの呼び出しを避け、ユニット内のロジックをテストするだけの場合は、モックを使用します。外部サービスを呼び出して実際の依存関係の呼び出しを実行する場合、または単に言うと、プログラムをそのまま実行し、特定のメソッドをスタブするだけの場合は、spyを使用します。それがスパイとモッキートのモックの違いです。

17
Vu Truong

ここで実行可能な例を作成しました https://www.surasint.com/mockito-with-spy/

ここにいくつかコピーします。

このコードのようなものがある場合:

public void transfer( DepositMoneyService depositMoneyService, 
                      WithdrawMoneyService withdrawMoneyService, 
                      double amount, String fromAccount, String toAccount) {
    withdrawMoneyService.withdraw(fromAccount,amount);
    depositMoneyService.deposit(toAccount,amount);
}

DepositMoneyServiceとWithdrawMoneyServiceをモックできるため、スパイを必要としない場合があります。

ただし、一部のレガシーコードでは、依存関係は次のようなコードにあります。

    public void transfer(String fromAccount, String toAccount, double amount) {
        this.depositeMoneyService = new DepositMoneyService();
        this.withdrawMoneyService = new WithdrawMoneyService();
        withdrawMoneyService.withdraw(fromAccount,amount);
        depositeMoneyService.deposit(toAccount,amount);
    }

はい、最初のコードに変更できますが、APIは変更されます。この方法が多くの場所で使用されている場合、それらすべてを変更する必要があります。

別の方法は、次のように依存関係を抽出できることです。

    public void transfer(String fromAccount, String toAccount, double amount){
        this.depositeMoneyService = proxyDepositMoneyServiceCreator();
        this.withdrawMoneyService = proxyWithdrawMoneyServiceCreator();
        withdrawMoneyService.withdraw(fromAccount,amount);
        depositeMoneyService.deposit(toAccount,amount);
    }

    DepositMoneyService proxyDepositMoneyServiceCreator() {
        return new DepositMoneyService();
    }

    WithdrawMoneyService proxyWithdrawMoneyServiceCreator() {
        return new WithdrawMoneyService();
    }

次に、次のような依存関係を注入するスパイを使用できます。

DepositMoneyService mockDepositMoneyService = mock(DepositMoneyService.class);
        WithdrawMoneyService mockWithdrawMoneyService = mock(WithdrawMoneyService.class);

    TransferMoneyService target = spy(new TransferMoneyService());

    doReturn(mockDepositMoneyService)
            .when(target)
            .proxyDepositMoneyServiceCreator();

    doReturn(mockWithdrawMoneyService)
            .when(target)
            .proxyWithdrawMoneyServiceCreator();

上記のリンクの詳細。

11

TL; DRバージョン

mockを使用すると、ベアボーンシェルインスタンスが作成されます。

List<String> mockList = Mockito.mock(ArrayList.class);

spyを使用すると、existingインスタンスを部分的にモックできます。

List<String> spyList = Mockito.spy(new ArrayList<String>());

Spyの典型的な使用例:クラスにはパラメーター化されたコンストラクターがあるため、最初にオブジェクトを作成します。

10
del bao

開始するのに最適な場所は、おそらく mockitoのドキュメント です。

一般的に、mockitoモックを使用すると、スタブを作成できます。

たとえば、そのメソッドが高価な操作を行う場合、スタブメソッドを作成します。たとえば、データベース接続を取得し、データベースから値を取得して呼び出し元に返します。 db接続の取得には30秒かかる場合があり、コンテキストの切り替え(またはテストの実行を停止)する可能性があるポイントまでテストの実行が遅くなります。

テストしているロジックがデータベース接続を気にしない場合、そのメソッドをハードコードされた値を返すスタブに置き換えることができます。

Mockitoスパイを使用すると、メソッドが他のメソッドを呼び出すかどうかを確認できます。これは、テスト対象のレガシーコードを取得しようとする場合に非常に役立ちます。

副作用を介して機能するメソッドをテストしている場合は、モッキートスパイを使用すると便利です。これにより、呼び出しが実際のオブジェクトに委任され、メソッドの呼び出し、呼び出された回数などを確認できます。

10

この推奨事項のシンプルさが気に入っています。

  • 安全で外部サービスの呼び出しを避け、ユニット内のロジックをテストするだけの場合は、mockを使用します。
  • 外部サービスを呼び出して実際の依存関係の呼び出しを実行する場合、または単にプログラムをそのまま実行して特定のメソッドをスタブする場合は、spy

ソース: https://javapointers.com/tutorial/difference-between-spy-and-mock-in-mockito/

一般的な違いは次のとおりです。

  • 依存関係のメソッドを直接スタブ化する場合、Mockその依存関係。
  • すべてのメソッドが必要なテスト値を返すように依存関係のデータをスタブ化する場合、Spyその依存関係。
2
leo9r

以下のポイントと比較して、以下のブログのURLをフォローできます。

  1. オブジェクト宣言
  2. メソッドがモックされていない場合
  3. メソッドがモックされたとき

https://onlyfullstack.blogspot.com/2019/02/mockito-mock-vs-spy.html メインチュートリアルのURL- https://onlyfullstack.blogspot.com/2019/ 02/mockito-tutorial.html

0
Saurabh Oza