Mockito:メソッドをスパイしようとすると、元のメソッドが呼び出されます


350

Mockito 1.9.0を使用しています。JUnitテストでクラスの単一メソッドの動作を模擬したいので、

final MyClass myClassSpy = Mockito.spy(myInstance);
Mockito.when(myClassSpy.method1()).thenReturn(myResults);

問題は、2行目myClassSpy.method1()で実際に呼び出されて例外が発生することです。モックを使用している唯一の理由は、後でmyClassSpy.method1()が呼び出されるたびに、実際のメソッドが呼び出されず、myResultsオブジェクトが返されるためです。

MyClassインターフェースでmyInstanceあり、重要な場合はその実装です。

このスパイ行為を修正するには何をする必要がありますか?


:これを見てくださいstackoverflow.com/a/29394497/355438
Lu55

回答:


609

公式ドキュメントを引用させてください:

実際のオブジェクトのスパイに関する重要な問題!

スパイをスタブするためにwhen(Object)を使用することが不可能な場合があります。例:

List list = new LinkedList();
List spy = spy(list);

// Impossible: real method is called so spy.get(0) throws IndexOutOfBoundsException (the list is yet empty)
when(spy.get(0)).thenReturn("foo");

// You have to use doReturn() for stubbing
doReturn("foo").when(spy).get(0);

あなたの場合、それは次のようになります:

doReturn(resulstIWant).when(myClassSpy).method1();

27
このメソッドを使用し、元のメソッドがまだ呼び出されている場合はどうなりますか?渡すパラメーターに問題があるのでしょうか?テスト全体は次のとおりです。pastebin.com / ZieY790Psendメソッドが呼び出されています
Evgeni Petrov

26
@EvgeniPetrov元のメソッドがまだ呼び出されている場合は、おそらく元のメソッドがfinalであるためです。Mockitoはfinalメソッドをモックしておらず、finalメソッドのモックについて警告することはできません。
MarcG、2014

1
これはdoThrow()でも可能ですか?
Gobliins、2015年

1
はい、残念ながら静的メソッドはモック不可能で「スパイ不可能」です。静的メソッドを処理するために行うことは、静的呼び出しをメソッドでラップし、そのメソッドでdoNothingまたはdoReturnを使用することです。シングルトンまたはスカラオブジェクトを使用して、ロジックの主要部分を抽象クラスに移動します。これにより、スパイを作成できるオブジェクトの代替テストクラス実装を使用できます。
Andrew Norman

24
そして、NOT finalおよびNOT staticメソッドがまだ呼び出されている場合はどうなりますか?
X-HuMan 2016年

27

私のケースは受け入れられた答えとは異なりました。そのパッケージに存在しないインスタンスのパッケージプライベートメソッドをモックしようとしました

package common;

public class Animal {
  void packageProtected();
}

package instances;

class Dog extends Animal { }

そしてテストクラス

package common;

public abstract class AnimalTest<T extends Animal> {
  @Before
  setup(){
    doNothing().when(getInstance()).packageProtected();
  }

  abstract T getInstance();
}

package instances;

class DogTest extends AnimalTest<Dog> {
  Dog getInstance(){
    return spy(new Dog());
  }

  @Test
  public void myTest(){}
}

コンパイルは正しいですが、テストを設定しようとすると、代わりに実際のメソッドが呼び出されます。

protectedメソッドまたはpublicメソッドを宣言すると問題が修正されますが、これはクリーンなソリューションではありません。


2
私は同様の問題に遭遇しましたが、testとpackage-privateメソッドは同じパッケージにありました。多分、Mockitoには一般的なパッケージプライベートメソッドに関する問題があると思います。
デイブ

22

私の場合、Mockito 2.0を使用して、実際の呼び出しをスタブ化するためにすべてのany()パラメーターをnullable()に変更する必要がありました。


2
321票のトップアンサーで落胆しないでください。これで私の問題は解決しました:)私はこれに数時間苦労しています!
クリスケッセル

3
これが私の答えでした。あなたの方法をからかったときに従うもののためにそれをさらに容易にするためには、構文は次のとおりです。 foo = Mockito.spy(foo); Mockito.doReturn(someValue).when(foo).methodToPrevent(nullable(ArgumentType.class));
Stryder

Mockito 2.23.4で、私はこのことを確認することができ、それは罰金で動作しますが、必要ではないanyeqのmatcherを。
vmaldosan

2
2.23.4 libバージョンで3つの異なるアプローチを試みました:any()、eq()、nullable()。後でのみ機能しました
ryzhman

こんにちは、あなたのソリューションは本当に素晴らしく、私にとってもうまくいきました。ありがとう
Dhiren Solanki

16

Tomasz Nurkiewiczの答えは、全体像を語っていないようです。

NB Mockitoバージョン:1.10.19。

私は非常にMockitoの新人なので、次の行動を説明することはできません。この答えを改善できる専門家がいる場合は、遠慮なくお問い合わせください。

ここで問題となっているメソッドはgetContentStringValueNOT finalNOT staticです。

この行元のメソッドを呼び出しますgetContentStringValue

doReturn( "dummy" ).when( im ).getContentStringValue( anyInt(), isA( ScoreDoc.class ));

この行元のメソッドを呼び出しませんgetContentStringValue

doReturn( "dummy" ).when( im ).getContentStringValue( anyInt(), any( ScoreDoc.class ));

答えられない理由で、を使用するisA()と、意図した(?)「メソッドを呼び出さない」動作がdoReturn失敗します。

ここに含まれるメソッドシグネチャを見てみましょう。これらは両方のstaticメソッドですMatchers。どちらもJavadocからreturnと言われていnullますが、それ自体を理解するのは少し困難です。おそらく、Classパラメーターとして渡されたオブジェクトが検査されますが、結果は計算されないか、破棄されません。それnullが任意のクラスを表すことができ、モックされたメソッドが呼び出されないことを期待していることを考えると、シグネチャはジェネリックパラメータではなくisA( ... )any( ... )単に返すことができませんでしたか?null<T>

とにかく:

public static <T> T isA(java.lang.Class<T> clazz)

public static <T> T any(java.lang.Class<T> clazz)

APIドキュメントには、これに関する手掛かりはありません。また、そのような「メソッドを呼び出さない」動作の必要性は「非常にまれ」であるとも言えます。個人的に私はいつもこのテクニックを使用しています。通常、モックには「シーンを設定」する数行が含まれていることに気づきます。その後、ステージングしたモックコンテキストでシーンを「再生」するメソッドを呼び出します。 。そして、景色と小道具を設定している間、俳優が左ステージに入り、心から演技を始めるのが最後に必要なのは...

しかし、これは私の給与水準をはるかに超えています...通過するモッキートの大祭司から説明を求めます...

*「一般的なパラメータ」は正しい用語ですか?


これが明快さを追加するか、問題をさらに混乱させるかどうかはわかりませんが、isA()とany()の違いはisA()が実際に型チェックを行うことですが、any()ファミリーのメソッドは単に型のキャストを避けるために作成されました引数。
Kevin Welker

@KevinWelkerありがとう。そして確かに、メソッド名はある自明の品質に欠けていません。ただし、適切に文書化していないため、天才のMockitoデザイナーを問題にします。間違いなく、モッキートに関する別の本を読む必要があります。PS実際には、「中級Mockito」を教えるためのリソースはほとんどないようです。
マイクげっ歯類

1
歴史は、anyXXメソッドは、型キャストのみを処理する方法として最初に作成されたというものです。次に、引数チェックを追加するよう提案されたときに、既存のAPIのユーザーを壊したくなかったため、isA()ファミリーを作成しました。any()メソッドは型チェックを一通り行う必要があることを知っていたため、Mockito 2.Xオーバーホールに他の重大な変更が導入されるまで(まだ試していません)、それらの変更を遅らせました。2.x +では、anyX()メソッドはisA()メソッドのエイリアスです。
Kevin Welker

ありがとうございました。これまで実行していたコードが突然静かに失敗するため、これは同時に複数のライブラリ更新を行う私たちにとって極めて重要な答えです。
Dex Stakker

6

スパイで問題を引き起こす可能性のあるもう1つのシナリオは、Spring BeanSpring Test Frameworkを使用)またはテスト中にオブジェクトをプロキシしている他のフレームワークをテストしている場合です。

@Autowired
private MonitoringDocumentsRepository repository

void test(){
    repository = Mockito.spy(repository)
    Mockito.doReturn(docs1, docs2)
            .when(repository).findMonitoringDocuments(Mockito.nullable(MonitoringDocumentSearchRequest.class));
}

上記のコードでは、SpringとMockitoの両方がMonitoringDocumentsRepositoryオブジェクトをプロキシしようとしますが、Springが最初であるため、findMonitoringDocumentsメソッドが実際に呼び出されます。リポジトリオブジェクトにスパイを配置した直後にコードをデバッグすると、デバッガー内で次のようになります。

repository = MonitoringDocumentsRepository$$EnhancerBySpringCGLIB$$MockitoMock$

@SpyBeanが助けに

代わりに@Autowiredアノテーションを使用する場合は、アノテーションを使用して@SpyBean上記の問題を解決します。SpyBeanアノテーションもリポジトリオブジェクトを挿入しますが、最初はMockitoによってプロキシされ、デバッガ内で次のようになります。

repository = MonitoringDocumentsRepository$$MockitoMock$$EnhancerBySpringCGLIB$

そしてここにコードがあります:

@SpyBean
private MonitoringDocumentsRepository repository

void test(){
    Mockito.doReturn(docs1, docs2)
            .when(repository).findMonitoringDocuments(Mockito.nullable(MonitoringDocumentSearchRequest.class));
}

1

スパイが元のメソッドを呼び出すもう1つの理由を発見しました。

誰かが模倣するアイデアを持っていた finalクラス発見しましたMockMaker

これは現在のメカニズムとは動作が異なり、これにはさまざまな制限があり、経験とユーザーフィードバックを収集したいので、この機能を使用可能にするには明示的にアクティブ化する必要がありました。これはsrc/test/resources/mockito-extensions/org.mockito.plugins.MockMaker、1行を含むファイルを作成することにより、mockito拡張メカニズムを介して実行できます。mock-maker-inline

ソース: https //github.com/mockito/mockito/wiki/What%27s-new-in-Mockito-2#mock-the-unmockable-opt-in-mocking-of-final-classesmethods

そのファイルをマージして自分のマシンに持ってきた後、私のテストは失敗しました。

その行(またはファイル)を削除するだけでspy()作業できました。


これが私の場合の理由でした、私は最終的なメソッドを模擬しようとしましたが、混乱していた明確なエラーメッセージなしで実際のメソッドを呼び出し続けました。
Bashar Ali Labadi

1

パーティーに少し遅れましたが、上記の解決策は私にはうまくいきませんでしたので、私の0.02 $を共有する

Mokcitoバージョン:1.10.19

MyClass.java

private int handleAction(List<String> argList, String action)

Test.java

MyClass spy = PowerMockito.spy(new MyClass());

以下は私にとってはうまくいきませんでした(実際のメソッドが呼び出されていました):

1。

doReturn(0).when(spy , "handleAction", ListUtils.EMPTY_LIST, new String());

2。

doReturn(0).when(spy , "handleAction", any(), anyString());

3。

doReturn(0).when(spy , "handleAction", null, null);

WORKEDに続いて:

doReturn(0).when(spy , "handleAction", any(List.class), anyString());

0

クラスのメソッドが呼び出されないようにする1つの方法は、メソッドをダミーでオーバーライドすることです。

    WebFormCreatorActivity activity = spy(new WebFormCreatorActivity(clientFactory) {//spy(new WebFormCreatorActivity(clientFactory));
            @Override
            public void select(TreeItem i) {
                log.debug("SELECT");
            };
        });

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.