web-dev-qa-db-ja.com

Mockitoを使用するときのモッキングとスパイの違いは何ですか?

Mockitoスパイの使用のユースケースは何ですか?

すべてのスパイのユースケースは、callRealMethodを使用してモックで処理できるように思えます。

私が見ることができる1つの違いは、ほとんどのメソッド呼び出しを本物にしたい場合、モックとスパイを使用するコードの行を節約します。それですか、それとも大きな画像を見逃していますか?

121
Victor Grazi

答えは ドキュメント にあります:

実際の部分モック(1.8.0以降)

最後に、メーリングリストで多くの内部討論と議論が行われた後、Mockitoに部分的なモックサポートが追加されました。以前は、コードの匂いとして部分モックを検討していました。しかし、部分的なモックの正当なユースケースが見つかりました-もっと読む: here

1.8より前のリリースでは、spy()は実際の部分的なモックを作成していなかったため、一部のユーザーにとって混乱が生じていました。

callRealMethod()spy()の後に導入されましたが、後方互換性を確保するために、もちろんspy()はそこに残されました。

そうでなければ、あなたは正しいです:スタブされない限り、スパイのすべての方法は本物です。 callRealMethod()が呼び出されない限り、モックのすべてのメソッドはスタブ化されます。一般的に、従来のcallRealMethod()の代わりにdoXxx().when()イディオムを使用することを強制しないため、when().thenXxx()の使用を好むでしょう。

87
JB Nizet

スパイとモックの違い

Mockitoがモックを作成するとき–実際のインスタンスからではなく、タイプのクラスからモックを作成します。モックは単純に、クラスの最低限のシェルインスタンスを作成し、そのインスタンスとの相互作用を追跡するために完全に装備されています。一方、スパイは既存のインスタンスをラップします。まだ通常のインスタンスと同じように動作します-唯一の違いは、それとのすべての相互作用を追跡するために装備されることです。

次の例では、ArrayListクラスのモックを作成します。

@Test
public void whenCreateMock_thenCreated() {
    List mockedList = Mockito.mock(ArrayList.class);

    mockedList.add("one");
    Mockito.verify(mockedList).add("one");

    assertEquals(0, mockedList.size());
}

ご覧のとおり、モックされたリストに要素を追加しても実際には何も追加されません。他の副作用なしにメソッドを呼び出すだけです。一方、スパイは異なる動作をします。実際には、addメソッドの実際の実装を呼び出し、要素を基になるリストに追加します。

@Test
public void whenCreateSpy_thenCreate() {
    List spyList = Mockito.spy(new ArrayList());
    spyList.add("one");
    Mockito.verify(spyList).add("one");

    assertEquals(1, spyList.size());
}

ここでは、size()メソッドを呼び出すとサイズが1になりますが、このsize()メソッドはモックされていないため、オブジェクトの実際の内部メソッドが呼び出されたと確信できます。 では、1はどこから来るのでしょうか?内部の実際のsize()メソッドは、size()がモック(またはスタブ)されていないため、エントリが実際のオブジェクトに追加されたと言います。

出典: http://www.baeldung.com/mockito-spy +自己メモ。

71
Saurabh Patil

8つのメソッドを持つオブジェクトがあり、7つの実際のメソッドを呼び出して1つのメソッドをスタブするテストがある場合、2つのオプションがあります。

  1. モックを使用すると、7 callRealMethodを呼び出して1つのメソッドをスタブしてセットアップする必要があります。
  2. spyを使用して、1つのメソッドをスタブ化して設定する必要があります

doCallRealMethod公式ドキュメント は、部分的なモックにスパイを使用することを推奨しています。

部分モックの詳細については、javadoc spy(Object)も参照してください。 Mockito.spy()は、部分的なモックを作成するための推奨される方法です。その理由は、spy()メソッドに渡されるオブジェクトを構築する責任があるため、正しく構築されたオブジェクトに対して実際のメソッドが呼び出されることを保証するからです。

32
user2412398

Spyは、レガシーコードの単体テストを作成する場合に役立ちます。

ここに実行可能な例を作成しました 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();

上記のリンクの詳細。

4