私は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
はそれが重要な場合の実装です。
このスパイ行為を修正するために何をする必要がありますか?
公式ドキュメント :
本物の物を盗むことに関する重要な注意!
ときどきスパイをスタブするために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();
私の場合は、受け入れられた答えとは異なりました。そのパッケージに存在しないインスタンスに対して、パッケージプライベートメソッドをモックしようとしていました
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は問題を解決しますが、問題ではありませんきれいな解決策。
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
の意図された(?)「メソッドを呼び出さない」振る舞いを失敗させます。
ここに含まれるメソッドシグネチャを見てみましょう。それらは両方ともstatic
のMatchers
メソッドです。両方とも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大祭司からの説明を招待します...
*「総称パラメータ」は正しい用語ですか?
私の場合は、Mockito 2.0を使用して、実際の呼び出しをスタブするために、すべてのany()パラメーターをnullable()に変更する必要がありました。
スパイで問題を引き起こす可能性があるもう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));
}
スパイがオリジナルのメソッドを呼び出すもう1つの理由を見つけました。
誰かがfinal
クラスを偽装するという考えを持ち、MockMaker
について見つけました。
これは現在のメカニズムとは異なる動作をし、これには異なる制限があり、経験とユーザーからのフィードバックを集めたいので、この機能は明示的にアクティブにして利用可能にする必要がありました。これは、mockito拡張メカニズムを介して、1行を含むファイル
src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
を作成することによって実行できます。mock-maker-inline
そのファイルをマージして自分のマシンに持ってきた後、私のテストは失敗しました。
その行(またはファイル)を削除するだけで、spy()
は動きました。
Scalaユーザーのための回答:doReturn
を最初に入れてもうまくいきません。この投稿を参照してください。
クラスのメソッドが呼び出されないようにする1つの方法は、ダミーでメソッドをオーバーライドすることです。
WebFormCreatorActivity activity = spy(new WebFormCreatorActivity(clientFactory) {//spy(new WebFormCreatorActivity(clientFactory));
@Override
public void select(TreeItem i) {
log.debug("SELECT");
};
});