[解決済み] Mockitoのマッチャーはどのように機能するのですか?
質問
Mockitoの引数マッチャー(例えば
any
,
argThat
,
eq
,
same
そして
ArgumentCaptor.capture()
など)はHamcrest matcherとは全く異なる振る舞いをします。
-
Mockitoのマッチャーは、マッチャーが使用されたずっと後に実行されるコードであっても、頻繁にInvalidUseOfMatchersExceptionを引き起こします。
-
Mockitoのマッチャーは、与えられたメソッドの1つの引数がマッチャーを使用する場合、すべての引数に対してMockitoのマッチャーの使用のみを要求するなど、奇妙なルールに従順である。
-
をオーバーライドすると、Mockito matcherがNullPointerExceptionを引き起こす可能性があります。
Answer
をオーバーライドした場合や(Integer) any()
などがあります。 -
Mockitoのマッチャーを使ったコードを特定の方法でリファクタリングすると、例外や予期せぬ動作が発生したり、完全に失敗したりすることがあります。
Mockitoのマッチャーはなぜこのような設計になっているのか、またどのように実装されているのか?
どのように解決するのですか?
Mockito マッチャー
は静的メソッドとそのメソッドの呼び出しで、これは
引数の代わりとなる
の呼び出しの際に
when
と
verify
.
ハムクレストマッチャー
(アーカイブ版) (あるいは Hamcrest-style matchers) は,ステートレスで汎用的なオブジェクトインスタンスです.
Matcher<T>
を実装し、メソッド
matches(T)
を公開し、オブジェクトが Matcher の条件にマッチする場合に true を返すメソッドを提供します。これらは副作用がないことを意図しており、一般的に以下のようなアサーションで使用されます。
/* Mockito */ verify(foo).setPowerLevel(gt(9000));
/* Hamcrest */ assertThat(foo.getPowerLevel(), is(greaterThan(9000)));
Hamcrestスタイルのマッチャーとは別に、Mockitoマッチャーが存在します。
マッチング式の記述が直接メソッド呼び出しにフィットするように
:
Mockitoのマッチャーが返す
T
ここで Hamcrest matcher のメソッドは Matcher オブジェクト (
Matcher<T>
).
Mockitoのマッチャーは、以下のような静的メソッドで呼び出されます。
eq
,
any
,
gt
そして
startsWith
について
org.mockito.Matchers
と
org.mockito.AdditionalMatchers
. また、Mockitoのバージョンによって変更されたアダプタもあります。
-
Mockito 1.xの場合。
Matchers
は一部の呼び出しを特徴としています (例えばintThat
またはargThat
) は、Hamcrest matcher を直接パラメータとして受け付ける Mockito matcher です。ArgumentMatcher<T>
拡張org.hamcrest.Matcher<T>
は、ハムクレストの内部表現で使用されていたものであり Hamcrest matcher の基底クラス であり、Mockito matcherのようなものではありません。 -
Mockito 2.0+では、MockitoはHamcrestに直接依存しなくなりました。
Matchers
の呼び出しは、以下のように表現されます。intThat
またはargThat
ラップArgumentMatcher<T>
を実装していないオブジェクトはorg.hamcrest.Matcher<T>
を実装していませんが、似たような方法で使用されています。のような Hamcrest アダプタはargThat
とintThat
はまだ利用可能ですが、移動してMockitoHamcrest
に移動しました。
マッチャーがHamcrestであるか、単にHamcrestスタイルであるかにかかわらず、このように適応させることができます。
/* Mockito matcher intThat adapting Hamcrest-style matcher is(greaterThan(...)) */
verify(foo).setPowerLevel(intThat(is(greaterThan(9000))));
上記の文の中に
foo.setPowerLevel
はメソッドで
int
.
is(greaterThan(9000))
は
Matcher<Integer>
として動作しません。
setPowerLevel
引数として機能しません。Mockitoのマッチャーである
intThat
はそのHamcrestスタイルのMatcherをラップし、その上に
int
を返すので
は
は引数として現れます。
gt(9000)
のようなMockitoのマッチャーは、例のコードの最初の行のように、その式全体を1つの呼び出しにラップします。
マッチャーが行うこと/返すこと
when(foo.quux(3, 5)).thenReturn(true);
引数マッチャーを使用しない場合、Mockitoは引数の値を記録し、その値と
equals
メソッドと比較します。
when(foo.quux(eq(3), eq(5))).thenReturn(true); // same as above
when(foo.quux(anyInt(), gt(5))).thenReturn(true); // this one's different
のようなマッチャを呼び出すと
any
あるいは
gt
(より大きい)の場合、Mockitoはマッチャー・オブジェクトを保存し、そのマッチャー・オブジェクトはMockitoにその等質性チェックをスキップさせ、選択したマッチを適用させます。の場合
argumentCaptor.capture()
の場合、後で検査するために代わりにその引数を保存するマッチャーを格納します。
マッチャーは
ダミー値
ゼロや空のコレクション、あるいは
null
. Mockitoは安全で適切なダミー値を返そうとします.
anyInt()
または
any(Integer.class)
または空の
List<String>
に対して
anyListOf(String.class)
. しかし,型消去のため,Mockitoは型情報を欠き
null
に対して
any()
または
argThat(...)
を自動アンボックスしようとすると、NullPointerException が発生することがあります。
null
プリミティブな値です。
のようなマッチャーは
eq
と
gt
はパラメータを受け取ります。理想的には、これらの値はスタブや検証を開始する前に計算されるべきです。他の呼び出しをモックしている最中にモックを呼び出すと、 スタブ動作に支障をきたす可能性があります。
Matcherのメソッドは戻り値として使用することはできません。
thenReturn(anyInt())
または
thenReturn(any(Foo.class))
といったように、Mockitoでは Mockitoはスタブ呼び出しの際にどのインスタンスを返すかを正確に知る必要があり、任意の戻り値を選んでくれるわけではありません。
実装の詳細
マッチャーは、(Hamcrestスタイルのオブジェクトマッチャーとして) 引数マッチャー格納 . MockitoCoreとMatchersはそれぞれ スレッドセーフモッキングプログレス インスタンスを所有し、その 静的に は MockingProgress インスタンスを保持する ThreadLocal を含んでいます。これは MockingProgressImpl であり、具体的な ArgumentMatcherStorageImpl . その結果、モックとマッチャーの状態は静的ですが、MockitoクラスとMatchersクラスの間で一貫してスレッドスコープされます。
ほとんどの matcher の呼び出しはこのスタックに追加されるだけですが、例外として、以下のような matcher があります。
and
,
or
そして
not
. これは完全に
の評価順序に完全に対応しています。
に完全に対応しており、メソッドを呼び出す前に引数を左から右に評価します。
when(foo.quux(anyInt(), and(gt(10), lt(20)))).thenReturn(true);
[6] [5] [1] [4] [2] [3]
これで
-
追加
anyInt()
をスタックに追加します。 -
追加する
gt(10)
をスタックに追加します。 -
追加する
lt(20)
をスタックに追加します。 -
削除する
gt(10)
とlt(20)
を追加しand(gt(10), lt(20))
. -
コール
foo.quux(0, 0)
を呼び出すと、(特にスタブがない限り)デフォルトの値であるfalse
. 内部的には,Mockitoはquux(int, int)
を最も新しい呼び出しとしてマークします。 -
呼び出し
when(false)
を呼び出すと、引数を破棄してスタブメソッドquux(int, int)
5で識別される。有効な状態はスタック長0(等号)または2(マッチャー)のみであり,スタック上に2つのマッチャーがあるので(ステップ1と4),モッキートはメソッドをany()
マッチャーを第1引数に、そしてand(gt(10), lt(20))
を返し、スタックをクリアします。
これはいくつかのルールを示しています。
-
Mockito は
quux(anyInt(), 0)
とquux(0, anyInt())
. どちらもquux(0, 0)
の呼び出しのように見えますが、スタック上に1つの int matcher があります。その結果、もし1つのマッチャーを使うなら、すべての引数にマッチしなければなりません。 -
呼び出し順は単に重要なだけでなく このすべてを機能させるものです。 . 変数へのマッチャーの抽出は、通常、呼び出し順を変更するため、うまくいきません。しかし、メソッドへのマッチャーの抽出は非常にうまくいきます。
int between10And20 = and(gt(10), lt(20)); /* BAD */ when(foo.quux(anyInt(), between10And20)).thenReturn(true); // Mockito sees the stack as the opposite: and(gt(10), lt(20)), anyInt(). public static int anyIntBetween10And20() { return and(gt(10), lt(20)); } /* OK */ when(foo.quux(anyInt(), anyIntBetween10And20())).thenReturn(true); // The helper method calls the matcher methods in the right order.
-
スタックは頻繁に変化するので、Mockitoはあまり注意深くそれを取り締まることができません。Mockitoやモックと対話するときにしかスタックをチェックできませんし、マッチャーがすぐに使われるか、偶然に放棄されるかを知らずにマッチャーを受け入れざるを得ません。理論的には、スタックは常に
when
またはverify
などがありますが、Mockitoは自動でチェックできません。 手動でチェックするにはMockito.validateMockitoUsage()
. -
の呼び出しで
when
を呼び出すと、Mockitoは実際に問題のメソッドを呼び出します。例外を投げるようにメソッドをスタブ化した場合(または非ゼロまたは非ヌル値を要求した場合)、例外がスローされます。doReturn
とdoAnswer
(など)する ではない は実際のメソッドを呼び出さないので、しばしば有用な代替手段となります。 -
スタブしている途中でモックメソッドを呼び出していた場合 (例えば
eq
マッチャーの答えを計算するため)、Mockito はスタックの長さを <項目 その を呼び出すことになり、失敗する可能性が高いです。 -
もしあなたが何か悪いことをしようとしたら、例えば 最終的なメソッドをスタブする/検証する のような悪いことをしようとすると、 Mockitoは本当のメソッド を呼び出し、さらにスタック上に余分なマッチャーを残します。 . そのため
final
メソッド呼び出しは例外を投げないかもしれませんが、その場合 InvalidUseOfMatchersExceptionが発生する可能性があります。 が発生する可能性があります。
よくある問題
-
-
もし matcher を使うなら、すべての引数が正確に1つの matcher 呼び出しを持っていること、そして
when
またはverify
を呼び出します。マッチャーは決してスタブの戻り値やフィールド/変数として使用してはいけません。 -
マッチャーの引数を提供する一部としてモックを呼び出していないことを確認してください。
-
matcherでfinalメソッドのスタブやベリファイをしようとしていないか確認してください。スタック上にマッチャーを残すのは素晴らしい方法で、finalメソッドが例外を投げない限り、モックしているメソッドがfinalであることに気づくのはこの時だけかもしれません。
-
-
プリミティブな引数でNullPointerExceptionが発生しました。
(Integer) any()
はnullを返しますがany(Integer.class)
は0を返します。NullPointerException
を期待している場合はint
を期待しているのであれば、それは整数ではありません。いずれにせよanyInt()
これはゼロを返し、オートボックスのステップもスキップします。 -
NullPointerExceptionなどの例外が発生します。 への呼び出し
when(foo.bar(any())).thenReturn(baz)
を呼び出すと、実際には を呼び出します。foo.bar(null)
で、NULL引数を受け取ったときに例外を投げるようにスタブしていたかもしれません。に切り替えるとdoReturn(baz).when(foo).bar(any())
に切り替えると、スタブ化した振る舞いをスキップします。 .
一般的なトラブルシューティング
-
使用方法 MockitoJUnitRunnerを使用する。 を使うか、明示的に
validateMockitoUsage
の中でtearDown
または@After
メソッド(これはランナーが自動的にやってくれるでしょう)を使用します。これは、マッチャーを誤って使用したかどうかを判断するのに役立ちます。 -
デバッグのために
validateMockitoUsage
を直接コードに追加してください。これはスタック上に何かあれば投げるので、悪い症状の良い警告となります。
関連
-
HttpClientがGZIP形式でない場合の対処法
-
[解決済み] JavaでInputStreamを読み込んでStringに変換するにはどうすればよいですか?
-
[解決済み] Java Mapの各エントリを効率的に反復処理するには?
-
[解決済み] Javaでメモリーリークを発生させるにはどうしたらいいですか?
-
[解決済み] Javaの「for each」ループはどのように機能するのですか?
-
[解決済み] Mockitoでvoidメソッドをモックする方法
-
[解決済み] Mockitoを使用して特定のメソッドが呼び出されていないことを確認するにはどうすればよいですか?
-
[解決済み] Mockitoは複数回呼び出されたメソッドの引数をキャプチャできますか?
-
[解決済み] Mockitoを使用して、あるメソッドをモックし、他のメソッドをモックしない
-
[解決済み] voidメソッドが例外を投げるかどうかをMockitoがテストする
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
java.util.NoSuchElementException 原因解析と解決方法
-
Enumとの組み合わせでswitchの使い方を一度覚えるために必要な定数式
-
eclipseにプロジェクトをインポートした後、Editorにmain typeが含まれない問題
-
Spring boot runs with Error creating bean with name 'entityManagerFactory' defined in class path resource
-
エラーの解決方法 jarfile XXX.jarにアクセスできません。
-
Methodのinvokeメソッド実装のJavaリフレクション
-
node js npm gruntインストール、elasticsearch-head 5.Xインストール
-
spring aop アドバイスからの Null 戻り値が、サマリーのプリミティブ戻り値と一致しない。
-
javax.net.ssl.SSLException: 読み取りエラー: ssl=0xdeae5100: システムコール中の I/O エラー、接続 res
-
[解決済み] ここで、誤った引数マッチャーが検出されました。Mockitoでは、検証やスタブ以外で引数マッチャーを使用することはできません。