コードを書いていると、常にvoidを返すメソッドがあり、何らかのテストケースでvoidメソッドをシミュレートする必要があります。では、どのようにすればよいのでしょうか?以下では、Mockitoを使ってこの要求を満たすために一緒に作業しましょう。
- Mockitoは、ユニットテストを書くための最もよく知られたモッキングフレームワークの1つです。
ボイドメソッドをシミュレートする理由
あるメソッドAがあって、その中で別のvoidメソッドBが使われているとします。さて、そのメソッドのテストケースを書くことになったとき、メソッドBが呼び出されることをどのようにテストしますか?また、正しいパラメータがメソッドBに渡されていますか?この場合、Mockitoはこの問題を解決するのに役立ちます。
例えば、UserServiceクラスがあります。このクラスには updateName() メソッドがあります。
public UserService{
public void updateName(Long id, String name){
userRepository.updateName(id, name);
}
}
ここで、UserService クラスの単体テストを作成し、userRepository をシミュレートします。ただし、このテストケースで検証する必要があるのは、userRepository の updateName() メソッドが正しいパラメータセットで呼び出されることだけです。このため、updateName() メソッドをシミュレートし、パラメータを取得して検証する必要があります。
ここで最も注意しなければならないのは、Mockitoの==when-then==の仕組みだけではvoidメソッドをシミュレートできないということです。なぜなら、Mockitoのwhen()メソッドは戻り値に適用され、メソッドの戻り値がvoidの場合には適用されないからです。
Mockitoでvoidメソッドをシミュレートする方法
Mockitoでは、インスタンス・メソッドを呼び出したり、voidメソッドをシミュレートするために使用できるさまざまなメソッドがあります。要件に応じて、これらのオプションのいずれかを使用してください:
- doNothing():voidメソッドの呼び出しを完全に無視します。
- doAnswer(): void メソッドをコールする際に、実行時あるいは複雑な処理を行います。
- doThrow(): シミュレートされた void メソッドが呼び出されたときに例外をスローします。
- doCallRealMethod(): 実際のメソッドをシミュレートして呼び出すのではありません。
doNothing() の使用法
void メソッド呼び出しを完全に無視したい場合は、doNothing() を使用します。
テストケースでは、シミュレートされたオブジェクトの各メソッドに対して doNothing がデフォルトの動作となります。したがって、パラメータを検証したくない場合は、doNothing を使用してもまったく問題ありません。
void メソッドに doNothing() を使用するデモ:
@Test
public void test001() {
doNothing().when(mockedUserRepository).updateName(anyLong(),anyString());
userService.updateName(1L,"FunTester");
verify(mockedUserRepository, times(1)).updateName(1L,"FunTester");
}
null メソッドには doNothing() を使用しないでください:
@Test
public void test002() {
userService.updateName(1L,"FunTester");
verify(mockedUserRepository, times(1)).updateName(1L,"FunTester");
}
doNothing() を使用したパラメータ・キャプチャの例
@Test
public void testUpdateNameUsingArgumentCaptor() {
ArgumentCaptor<Long> idCapture = ArgumentCaptor.forClass(Long.class);
ArgumentCaptor<String> nameCapture = ArgumentCaptor.forClass(String.class);
doNothing().when(mockedUserRepository).updateName(idCapture.capture(),nameCapture.capture());
userService.updateName(1L,"FunTester");
assertEquals(1L, idCapture.getValue());
assertEquals("FunTester", nameCapture.getValue());
}
void メソッドに対する doAnswer() の使用
実際のメソッドを呼び出したくなく、実行時に何らかの操作を行う必要がある場合は、doAnswer() を使用します。
doAnswer()を使ってパラメータを表示し、検証するデモを以下に示します。
@Test
public void testUpdateNameUsingDoAnswer() {
doAnswer(invocation -> {
long id = invocation.getArgument(0);
String name = invocation.getArgument(1);
System.out.println("called for id: "+id+" and name: "+name);
assertEquals(1L, id);
assertEquals("FunTester", name);
return null;
}).when(mockedUserRepository).updateName(anyLong(),anyString());
userService.updateName(1L,"FunTester");
verify(mockedUserRepository, times(1)).updateName(1L,"FunTester");
}
doThrow() による例外の発生
メソッドが呼ばれたときに例外を発生させるには、モッキングの doThrow() メソッドを使います。
InvalidParamException
idにnullを指定してupdateName()メソッドを呼び出すと、nullが発生します。
@Test(expected = InvalidParamException.class)
public void testUpdateNameThrowExceptionWhenIdNull() {
doThrow(new InvalidParamException())
.when(mockedUserRepository).updateName(null,anyString();
userService.updateName(null,"FunTester");
}
doCallRealMethod() によるリアルメソッド呼び出し
シミュレートされたオブジェクトから実際のメソッドを呼び出す必要がある場合があります。この場合、doNothig() がデフォルトの動作であるため、doCallRealMethod() が必要になります。
次の例では、userRepository の実際のメソッドが、シミュレートされたオブジェクトであっても呼び出されます。
@Test
public void testUpdateNameCallRealRepositoryMethod() {
doCallRealMethod().when(mockedUserRepository).updateName(anyLong(), anyString());
userService.updateName(1L,"実際の呼び出し方法");
verify(mockedUserRepository, times(1)).add(1L,"実際の呼び出し方法");
}