JMockitの期待へのガイド
1イントロ
この記事は、JMockitシリーズの2回目の記事です。/jmockit-101[最初の記事]というリンクをお読みになるとよいでしょう。私たちはあなたがすでにJMockitの基本に精通していると想定しています。
今日、私たちはより深く行き、期待に焦点を合わせます。より具体的または一般的な引数の一致を定義する方法、および値を定義するためのより高度な方法を示します。
2引数値の一致
以下のアプローチは、期待値と検証の両方に適用されます。
2.1. 「任意」のフィールド
JMockitは引数マッチングをより一般的にするためのユーティリティフィールドのセットを提供します。これらのユーティリティの1つは
anyX
フィールドです。
これらは任意の値が渡されたことをチェックし、各プリミティブ型(および対応するラッパークラス)に1つ、文字列に1つ、そして
Object
型の「ユニバーサル」なものがあることを確認します。
例を見てみましょう。
public interface ExpectationsCollaborator {
String methodForAny1(String s, int i, Boolean b);
void methodForAny2(Long l, List<String> lst);
}
@Test
public void test(@Mocked ExpectationsCollaborator mock) throws Exception {
new Expectations() {{
mock.methodForAny1(anyString, anyInt, anyBoolean);
result = "any";
}};
Assert.assertEquals("any", mock.methodForAny1("barfooxyz", 0, Boolean.FALSE));
mock.methodForAny2(2L, new ArrayList<>());
new FullVerifications() {{
mock.methodForAny2(anyLong, (List<String>) any);
}};
}
any
フィールドを使用するときは、期待される型にキャストする必要があることを考慮に入れる必要があります。フィールドの完全なリストはhttp://jmockit.github.io/tutorial/Mocking.html#expectation[documentation]にあります。
2.2. 「With」メソッド
JMockitには、一般的な引数の照合に役立ついくつかのメソッドもあります。それらは
withX
メソッドです。
これらは
anyX
フィールドよりも少し高度なマッチングを可能にします。
ここでは、
foo
、1に等しくない整数、null以外の
Boolean
、および
List
クラスのインスタンスのいずれかを含む文字列でトリガーされるメソッドに対する期待値を定義する例を示します。
public interface ExpectationsCollaborator {
String methodForWith1(String s, int i);
void methodForWith2(Boolean b, List<String> l);
}
@Test
public void testForWith(@Mocked ExpectationsCollaborator mock) throws Exception {
new Expectations() {{
mock.methodForWith1(withSubstring("foo"), withNotEqual(1));
result = "with";
}};
assertEquals("with", mock.methodForWith1("barfooxyz", 2));
mock.methodForWith2(Boolean.TRUE, new ArrayList<>());
new Verifications() {{
mock.methodForWith2(withNotNull(), withInstanceOf(List.class));
}};
}
JMockitのhttp://jmockit.github.io/tutorial/Mocking.html#expectation[ドキュメント]に
withX
メソッドの完全なリストがあります。
特別な
with(Delegate)
と
withArgThat(Matcher)
がそれぞれのサブセクションでカバーされることを考慮してください。
2.3. NULLはNULLではありません
null
は、
null
がモックに渡されている引数を定義するために使用されていないということです。
実際には、
null
は
構文糖
として使用され、任意のオブジェクトが渡されることを定義します(したがって、参照型のパラメータにのみ使用できます)。特定のパラメータが
null
参照を受け取ることを明確に確認するために、
withNull()
matcherを使用できます。
次の例では、モックの振る舞いを定義します。モックは、渡された引数が任意の文字列、任意のList、および
null
参照である場合にトリガーされる必要があります。
public interface ExpectationsCollaborator {
String methodForNulls1(String s, List<String> l);
void methodForNulls2(String s, List<String> l);
}
@Test
public void testWithNulls(@Mocked ExpectationsCollaborator mock){
new Expectations() {{
mock.methodForNulls1(anyString, null);
result = "null";
}};
assertEquals("null", mock.methodForNulls1("blablabla", new ArrayList<String>()));
mock.methodForNulls2("blablabla", null);
new Verifications() {{
mock.methodForNulls2(anyString, (List<String>) withNull());
}};
}
違いに注意してください。
null
は任意のリストを意味し、
withNull()
はリストへの
null
参照を意味します。特に、これにより、宣言されたパラメータ型に値をキャストする必要がなくなります(3番目の引数はキャストする必要がありますが、2番目の引数はキャストしないでください)。
これを使用できる唯一の条件は、少なくとも1つの明示的な引数マッチャーが(
with
メソッドまたは
any
フィールドのいずれかの)期待値に対して使用されていることです。
2.4. 「時間」フィールド
ときどき、モックされたメソッドに期待される呼び出し数を
制約
** したいです。このために、JMockitには、予約語
times
、
minTimes
、および
maxTimes
があります(3つとも、負でない整数のみを許可します)。
public interface ExpectationsCollaborator {
void methodForTimes1();
void methodForTimes2();
void methodForTimes3();
}
@Test
public void testWithTimes(@Mocked ExpectationsCollaborator mock) {
new Expectations() {{
mock.methodForTimes1(); times = 2;
mock.methodForTimes2();
}};
mock.methodForTimes1();
mock.methodForTimes1();
mock.methodForTimes2();
mock.methodForTimes3();
mock.methodForTimes3();
mock.methodForTimes3();
new Verifications() {{
mock.methodForTimes3(); minTimes = 1; maxTimes = 3;
}};
}
この例では、
methodForTimes1()
の呼び出しは、厳密に2回(1回ではなく3回ではなく2回)
= 2; __という行を使用して行う必要があると定義しました。
次に、デフォルトの動作(繰り返し制約が指定されていない場合は
minTimes = 1;
が使用される)を使用して、少なくとも1回の呼び出しが__methodForTimes2()。に対して行われることを定義します。
最後に、
minTimes = 1;
に続けて
maxTimes = 3;
を使用して、
methodForTimes3()
に対して1〜3回の呼び出しが発生するように定義しました。
minTimes
が最初に割り当てられている限り、
minTimes
と
maxTimes
の両方を同じ期待値に指定できることを考慮に入れてください。一方、
times
は単独でしか使用できません。
2.5. カスタム引数のマッチング
引数のマッチングは、単に値を指定したり、事前定義されたユーティリティ(
anyX
または
withX
)を使用するだけでは直接的ではない場合があります。
そのような場合、JMockitはhttp://hamcrest.org/[Hamcrest]の
Matcher
インターフェースに依存しています。特定のテストシナリオ用のマッチャーを定義し、そのマッチャーを
withArgThat()
呼び出しで使用するだけです。
特定のクラスを渡されたオブジェクトと照合する例を見てみましょう。
public interface ExpectationsCollaborator {
void methodForArgThat(Object o);
}
public class Model {
public String getInfo(){
return "info";
}
}
@Test
public void testCustomArgumentMatching(@Mocked ExpectationsCollaborator mock) {
new Expectations() {{
mock.methodForArgThat(withArgThat(new BaseMatcher<Object>() {
@Override
public boolean matches(Object item) {
return item instanceof Model && "info".equals(((Model) item).getInfo());
}
@Override
public void describeTo(Description description) { }
}));
}};
mock.methodForArgThat(new Model());
}
3返り値
戻り値を見てみましょう。以下のアプローチは
Verifications
に戻り値を定義できないため、
Expectations
にのみ適用されることに注意してください。
3.1. 結果と戻り値(…)
JMockitを使用するときは、モックメソッドの呼び出しの期待される結果を定義する3つの異なる方法があります。 3つすべてのうち、最初の2つ(最も単純なもの)について説明します。これは、日常の使用例の90%を確実にカバーします。
これら2つは
result
フィールドと
returns(Object …)
メソッドです。
-
result
フィールドを使うと、任意のフィールドに対して
one
戻り値を定義できます。
非void戻りモックメソッドこの戻り値は、スローされる例外になることもあります(今回は、non-voidとvoidを返すメソッドの両方に対して機能します)。
-
** 結果を返すために、いくつかの
result
フィールドの割り当てが可能です。 -
複数のメソッド呼び出しに対して
複数の値
(返される値と返されるエラーの両方を混在させることができます)。 -
**
result
にリストを代入するときにも同じ振る舞いが達成されます
または値の配列(モックメソッドの戻り値の型と同じ型、ここでは例外ありません)。
-
returns(Object …)
メソッドは返すための
構文糖
です
同じ時間のいくつかの値。
これはコードスニペットでより簡単に示されます。
public interface ExpectationsCollaborator{
String methodReturnsString();
int methodReturnsInt();
}
@Test
public void testResultAndReturns(@Mocked Foo mock){
new StrictExpectations() {{
mock.methodReturnsString();
result = "foo";
result = new Exception();
result = "bar";
mock.methodReturnsInt(); result = new int[]{ 1, 2, 3 };
mock.methodReturnsString(); returns("foo", "bar");
mock.methodReturnsInt(); result = 1;
}};
assertEquals("Should return foo", "foo", mock.methodReturnsString());
try {
mock.methodReturnsString();
} catch (Exception e) { }
assertEquals("Should return bar", "bar", mock.methodReturnsString());
assertEquals("Should return 1", 1, mock.methodReturnsInt());
assertEquals("Should return 2", 2, mock.methodReturnsInt());
assertEquals("Should return 3", 3, mock.methodReturnsInt());
assertEquals("Should return foo", "foo", mock.methodReturnsString());
assertEquals("Should return bar", "bar", mock.methodReturnsString());
assertEquals("Should return 1", 1, mock.methodReturnsInt());
}
この例では、
methodReturnsString()
の最初の3回の呼び出しで期待される戻り値が(順に)
“ foo”
、例外、および
“ bar”
であると定義しました。
result
フィールドへの3つの異なる割り当てを使用してこれを達成しました。
そして14行目で、4回目と5回目の呼び出しでは、
returns(Object …)
メソッドを使用して
“ foo”
と
“ bar”
を返すように定義しました。
methodReturnsInt()
では、
result
フィールドに異なる結果を持つ配列を割り当てることによって1、2、最後に3を返すように
line 13
で定義し、
line 15
では、結果フィールド。
ご覧のとおり、モックメソッドの戻り値を定義する方法はいくつかあります。
3.2. 委任者
記事を終わらせるために、戻り値を定義する3つ目の方法である
Delegate
インターフェースについて説明します。このインタフェースは、モックメソッドを定義するときに、より複雑な戻り値を定義するために使用されます。
説明のための例を見てみましょう。
public interface ExpectationsCollaborator {
Object methodForDelegate(int i);
}
@Test
public void testDelegate(@Mocked ExpectationsCollaborator mock) {
new Expectations() {{
mock.methodForDelegate(anyInt);
result = new Delegate() {
public int delegate(int i) throws Exception {
if(i < 3) {
return 5;
} else {
throw new Exception();
}
}
};
}};
assertEquals("Should return 5", 5, mock.methodForDelegate(1));
try {
mock.methodForDelegate(3);
} catch (Exception e) { }
}
デリゲータを使用する方法は、それに対して新しいインスタンスを作成し、それを
returns
フィールドに割り当てることです。この新しいインスタンスでは、モックメソッドと同じパラメータと戻り値の型を使用して新しいメソッドを作成する必要があります(任意の名前を使用できます)。この新しいメソッドの中では、目的の値を返すために、必要な実装を使用してください。
この例では、モックメソッドに渡された値が
3
より小さく、それ以外の場合は例外がスローされるときに
5
が返されるように実装しました(2回目の呼び出しは戻り値を定義することによってデフォルトの動作を失ったので期待されています)。
これはかなりたくさんのコードのように思えるかもしれませんが、同じ場合には、それが私たちが望む結果を達成する唯一の方法になるでしょう。
4結論
これで、私たちは日常のテストのための期待と検証を作成するために必要なものすべてを実際に示しました。
私たちはもちろんJMockitに関するより多くの記事を公開するので、もっともっと学ぶように注意してください。
そして、いつものように、このチュートリアルの完全な実装はhttps://github.com/eugenp/tutorials/tree/master/testing-modules/mocks[GitHubプロジェクト]にあります。