web-dev-qa-db-ja.com

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

私は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はそれが重要な場合の実装です。

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

287
Dave

公式ドキュメント

本物の物を盗むことに関する重要な注意!

ときどきスパイをスタブするために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();
498

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

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は問題を解決しますが、問題ではありませんきれいな解決策。

24
Maragues

Tomasz Nurkiewiczの答えは物語全体を伝えているわけではないようです!

NB Mockitoのバージョン:1.10.19。

私は非常にMockitoの初心者なので、次のような振る舞いを説明することはできません。この回答を改善できる専門家がいるのであれば、お気軽にどうぞ。

ここで問題となっているメソッドgetContentStringValueは、NOTfinalおよびNOTstaticです。

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

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

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

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

私が答えることができない理由のために、isA()を使うことはdoReturnの意図された(?)「メソッドを呼び出さない」振る舞いを失敗させます。

ここに含まれるメソッドシグネチャを見てみましょう。それらは両方ともstaticMatchersメソッドです。両方ともJavadocによってnullが返されると言われていますが、それ自体で頭を動かすのは少し難しいです。おそらく、パラメータとして渡されたClassオブジェクトが調べられますが、結果は決して計算されないか破棄されません。 nullが任意のクラスを表すことができ、モックされたメソッドが呼び出されないことを望んでいると仮定すると、isA( ... )およびany( ... )のシグニチャーは、総称パラメーター* <T>ではなくnullを返すことができませんか?

とにかく:

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

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

APIドキュメンテーションはこれについて少しの手がかりも与えません。また、このような「メソッドを呼び出さない」動作の必要性は「非常にまれ」です。私は個人的にはいつもこのテクニックを使っています:通常、モックには「シーンを設定する」数行が続き、その後「再生する」メソッドを呼び出すことになります。あなたが上演したモックコンテキストでのシーン...そしてあなたが風景と小道具をセットアップしている間あなたが望む最後のことは俳優が左のステージに入って彼らの心を演じ始めることです...

しかし、これは私の給与等級を超えた方法です...私は、パスしているMockito大祭司からの説明を招待します...

*「総称パラメータ」は正しい用語ですか?

14
mike rodent

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

14
ejaenv

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

@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));
}
3

スパイがオリジナルのメソッドを呼び出すもう1つの理由を見つけました。

誰かがfinalクラスを偽装するという考えを持ち、MockMakerについて見つけました。

これは現在のメカニズムとは異なる動作をし、これには異なる制限があり、経験とユーザーからのフィードバックを集めたいので、この機能は明示的にアクティブにして利用可能にする必要がありました。これは、mockito拡張メカニズムを介して、1行を含むファイルsrc/test/resources/mockito-extensions/org.mockito.plugins.MockMakerを作成することによって実行できます。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()は動きました。

1
Matruskan

Scalaユーザーのための回答:doReturnを最初に入れてもうまくいきません。この投稿を参照してください。

0
Nick Resnick

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

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