回答:
実際の部分モック(1.8.0以降)
最後に、メーリングリストでの内部での多くの議論と議論の後、部分的なモックサポートがMockitoに追加されました。以前は、コードの匂いとして部分的なモックを考慮していました。ただし、部分的なモックの正当な使用例が見つかりました。
1.8より前のリリースでは、spy()は実際の部分モックを生成せず、一部のユーザーを混乱させていました。スパイの詳細については、こちらまたはjavadocのspy(Object)メソッドをご覧ください。
callRealMethod()
はの後に導入されましたがspy()
、後方互換性を確保するために、spy()はもちろん残されました。
そうでなければ、あなたは正しいです:スパイのすべての方法は、スタブされない限り本物です。モックのすべてのメソッドは、callRealMethod()
呼び出されない限りスタブされます。一般的に、私はの使用を好みcallRealMethod()
ます。これdoXxx().when()
は、従来の代わりにイディオムを使用することを強制しないためです。when().thenXxx()
スパイとモックの違い
Mockitoがモックを作成するとき–実際のインスタンスからではなく、タイプのクラスから作成します。モックは、クラスとの相互作用を追跡するために完全に装備された、クラスの必要最小限のシェルインスタンスを作成するだけです。一方、スパイは既存のインスタンスをラップします。それでも、通常のインスタンスと同じように動作します。唯一の違いは、それとのすべての相互作用を追跡するように装備されることです。
次の例では、ArrayListクラスのモックを作成します。
@Test
public void whenCreateMock_thenCreated() {
List mockedList = Mockito.mock(ArrayList.class);
mockedList.add("one");
Mockito.verify(mockedList).add("one");
assertEquals(0, mockedList.size());
}
ご覧のとおり、モックリストに要素を追加しても実際には何も追加されません。他の副作用なしでメソッドを呼び出すだけです。一方、スパイは異なる動作をします–実際にaddメソッドの実際の実装を呼び出し、要素を基になるリストに追加します。
@Test
public void whenCreateSpy_thenCreate() {
List spyList = Mockito.spy(new ArrayList());
spyList.add("one");
Mockito.verify(spyList).add("one");
assertEquals(1, spyList.size());
}
ここで、オブジェクトの実際の内部メソッドが呼び出されたと言えます。size()メソッドを呼び出すと、サイズは1になりますが、このsize()メソッドはモックされていません。では、1はどこから来るのでしょうか。size()がモック(またはスタブ)されていないため、内部の実際のsize()メソッドが呼び出されるため、エントリが実際のオブジェクトに追加されたと言えます。
出典:http : //www.baeldung.com/mockito-spy + self notes。
mockedList.size()
戻るの0
ですか?メソッドの戻り値の型が与えられた場合、それは単なるデフォルト値ですか?
mockedList.size()
返し、Javaのint
デフォルト値int
は0です。のassertEquals(0, mockedList.size());
後mockedList.clear();
に実行しようとすると、結果は同じままです。
8つのメソッドを持つオブジェクトがあり、7つの実際のメソッドを呼び出して1つのメソッドをスタブするテストがある場合、2つのオプションがあります。
spy
1つのメソッドをスタブして設定する必要がありますの公式ドキュメントでdoCallRealMethod
は、部分的なモックにスパイを使用することを推奨しています。
部分モックの詳細については、javadoc spy(Object)も参照してください。Mockito.spy()は、部分的なモックを作成するための推奨される方法です。その理由は、spy()メソッドに渡されたオブジェクトを作成する責任があるため、正しく作成されたオブジェクトに対して実際のメソッドが呼び出されることが保証されるためです。
スパイは、レガシーコードの単体テストを作成する場合に役立ちます。
ここで実行可能な例をhttps://www.surasint.com/mockito-with-spy/で作成しました。その一部をここにコピーします。
このコードのようなものがあれば:
public void transfer( DepositMoneyService depositMoneyService, WithdrawMoneyService withdrawMoneyService,
double amount, String fromAccount, String toAccount){
withdrawMoneyService.withdraw(fromAccount,amount);
depositMoneyService.deposit(toAccount,amount);
}
DepositMoneyServiceとWithdrawMoneyServiceをモックするだけなので、スパイは必要ないかもしれません。
しかし、いくつかのレガシーコードでは、依存関係は次のようなコードにあります。
public void transfer(String fromAccount, String toAccount, double amount){
this.depositeMoneyService = new DepositMoneyService();
this.withdrawMoneyService = new WithdrawMoneyService();
withdrawMoneyService.withdraw(fromAccount,amount);
depositeMoneyService.deposit(toAccount,amount);
}
はい、最初のコードに変更できますが、その後APIが変更されます。この方法が多くの場所で使用されている場合は、それらすべてを変更する必要があります。
代わりに、次のように依存関係を抽出できます。
public void transfer(String fromAccount, String toAccount, double amount){
this.depositeMoneyService = proxyDepositMoneyServiceCreator();
this.withdrawMoneyService = proxyWithdrawMoneyServiceCreator();
withdrawMoneyService.withdraw(fromAccount,amount);
depositeMoneyService.deposit(toAccount,amount);
}
DepositMoneyService proxyDepositMoneyServiceCreator() {
return new DepositMoneyService();
}
WithdrawMoneyService proxyWithdrawMoneyServiceCreator() {
return new WithdrawMoneyService();
}
次に、次のように依存関係を挿入するスパイを使用できます。
DepositMoneyService mockDepositMoneyService = mock(DepositMoneyService.class);
WithdrawMoneyService mockWithdrawMoneyService = mock(WithdrawMoneyService.class);
TransferMoneyService target = spy(new TransferMoneyService());
doReturn(mockDepositMoneyService)
.when(target).proxyDepositMoneyServiceCreator();
doReturn(mockWithdrawMoneyService)
.when(target).proxyWithdrawMoneyServiceCreator();
上記のリンクの詳細。
Mock
ベアダブルオブジェクトです。このオブジェクトには同じメソッドシグネチャがありますが、実現は空で、デフォルト値-0およびnullを返します
Spy
複製されたdoubleオブジェクトです。新しいオブジェクトは実際のオブジェクトに基づいて複製されますが、それを模擬する可能性があります
class A {
String foo1() {
foo2();
return "RealString_1";
}
String foo2() {
return "RealString_2";
}
void foo3() {
foo4();
}
void foo4() {
}
}
@Test
public void testMockA() {
//given
A mockA = Mockito.mock(A.class);
Mockito.when(mockA.foo1()).thenReturn("MockedString");
//when
String result1 = mockA.foo1();
String result2 = mockA.foo2();
//then
assertEquals("MockedString", result1);
assertEquals(null, result2);
//Case 2
//when
mockA.foo3();
//then
verify(mockA).foo3();
verify(mockA, never()).foo4();
}
@Test
public void testSpyA() {
//given
A spyA = Mockito.spy(new A());
Mockito.when(spyA.foo1()).thenReturn("MockedString");
//when
String result1 = spyA.foo1();
String result2 = spyA.foo2();
//then
assertEquals("MockedString", result1);
assertEquals("RealString_2", result2);
//Case 2
//when
spyA.foo3();
//then
verify(spyA).foo3();
verify(spyA).foo4();
}