Void型の戻り値を持つメソッドをモックする方法
オブザーバパターンを実装しましたが、Mockitoでモックすることはできません。
そして私はインターネットで例を見つけようとしましたが、成功しませんでした。
私のクラスはこんな感じです:
public class World {
List<Listener> listeners;
void addListener(Listener item) {
listeners.add(item);
}
void doAction(Action goal,Object obj) {
setState("i received");
goal.doAction(obj);
setState("i finished");
}
private string state;
//setter getter state
}
public class WorldTest implements Listener {
@Test public void Word{
World w= mock(World.class);
w.addListener(this);
...
...
}
}
interface Listener {
void doAction();
}
システムはモックで起動されません。 =(上記のシステム状態を表示したい。それらに従ってアサーションを行う。
いわゆる問題の解決策は、spy
Mockito.mock(..) の代わりにmock
Mockito.spy(...) を使用することです。
スパイは私たちが部分的にモックすることを可能にします。 Mockitoはこの件が得意です。まだ完成していないクラスがあるので、このようにしてこのクラス内の必要な場所をモックします。
Mockito APIドキュメント を見てください。リンクされたドキュメントが言及しているように(Point#12)、Mockitoフレームワークからvoidメソッドを模擬するために、doThrow()
、doAnswer()
、doNothing()
、doReturn()
のいずれかのメソッドファミリーを使用することができます。
例えば、
Mockito.doThrow(new Exception()).when(instance).methodName();
あるいはフォローアップ行動と組み合わせたい場合は、
Mockito.doThrow(new Exception()).doNothing().when(instance).methodName();
以下のクラスWorldでセッターsetState(String s)
をモックするのを見ていると仮定すると、コードはdoAnswer
メソッドを使ってsetState
をモックします。
World mockWorld = mock(World.class);
doAnswer(new Answer<Void>() {
public Void answer(InvocationOnMock invocation) {
Object[] args = invocation.getArguments();
System.out.println("called with arguments: " + Arrays.toString(args));
return null;
}
}).when(mockWorld).setState(anyString());
私はその質問に対するもっと簡単な答えを見つけたと思います、本当のメソッドをただ一つのメソッドのために呼び出すために(それが無効な戻り値を持っているとしても)あなたはこれをすることができます:
Mockito.doCallRealMethod().when(<objectInstance>).<method>();
<objectInstance>.<method>();
あるいは、そのクラスのすべてのメソッドに対して実際のメソッドを呼び出すこともできます。
<Object> <objectInstance> = mock(<Object>.class, Mockito.CALLS_REAL_METHODS);
@sateeshが言ったことに加えて、テストがそれを呼び出さないようにするために単にvoidメソッドをモックしたいときは、このようにSpy
を使うことができます。
World world = new World();
World spy = Mockito.spy(world);
Mockito.doNothing().when(spy).methodToMock();
テストを実行するときは、spy
オブジェクトではなくworld
オブジェクトに対してtestのメソッドを必ず呼び出してください。例えば:
assertEquals(0,spy.methodToTestThatShouldReturnZero());
まず第一に:あなたは常にmockito staticをインポートするべきです。こうすればコードはずっと読みやすく(そして直感的に)なります:
import static org.mockito.Mockito.*;
部分的なモックを作成し、それでも元の機能を残りのモックに維持するために、 "Spy"を提供しています。
あなたは次のようにそれを使用することができます:
private World world = spy(World.class);
メソッドが実行されないようにするには、次のようにします。
doNothing().when(someObject).someMethod(anyObject());
メソッドにカスタム動作を与えるには、 "thenReturn"と共に "when"を使用します。
doReturn("something").when(this.world).someMethod(anyObject());
他の例については、ドキュメントの中のmockitoサンプルを見つけてください。
mockitoでvoidメソッドをモックする方法 - 2つの選択肢があります。
doAnswer
- モックされたvoidメソッドに何かをさせたい場合(voidにもかかわらず振る舞いをモックする).doThrow
- モックのvoidメソッドから例外をスローしたい場合はMockito.doThrow()
があります。以下はその使い方の例です(理想的なユースケースではありませんが、基本的な使い方を説明したいだけです)。
@Test
public void testUpdate() {
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
Object[] arguments = invocation.getArguments();
if (arguments != null && arguments.length > 1 && arguments[0] != null && arguments[1] != null) {
Customer customer = (Customer) arguments[0];
String email = (String) arguments[1];
customer.setEmail(email);
}
return null;
}
}).when(daoMock).updateEmail(any(Customer.class), any(String.class));
// calling the method under test
Customer customer = service.changeEmail("[email protected]", "[email protected]");
//some asserts
assertThat(customer, is(notNullValue()));
assertThat(customer.getEmail(), is(equalTo("[email protected]")));
}
@Test(expected = RuntimeException.class)
public void testUpdate_throwsException() {
doThrow(RuntimeException.class).when(daoMock).updateEmail(any(Customer.class), any(String.class));
// calling the method under test
Customer customer = service.changeEmail("[email protected]", "[email protected]");
}
}
Mockitoを使った mock および test void メソッドの使い方の詳細については、 Mockitoを使ってモックする方法(例を含む総合ガイド)
束に別の回答を追加する.
スパイを使用したくない場合はdoAnswerメソッドを呼び出す必要があります。しかし、あなたは必ずしもあなた自身の 回答 をロールする必要はありません。いくつかのデフォルトの実装があります。特に、 CallsRealMethods 。
実際には、これは次のようになります。
doAnswer(new CallsRealMethods()).when(mock)
.voidMethod(any(SomeParamClass.class));
または
doAnswer(Answers.CALLS_REAL_METHODS.get()).when(mock)
.voidMethod(any(SomeParamClass.class));
Java 8では、org.mockito.Mockito.doAnswer
の静的インポートがあると仮定すると、これは少しきれいになります。
doAnswer(i -> {
// Do stuff with i.getArguments() here
return null;
}).when(*mock*).*method*(*methodArguments*);
return null;
は重要で、それがなければコンパイルはdoAnswer
の適切なオーバーライドを見つけることができないので、かなりあいまいなエラーで失敗します。
たとえば、execute()
に渡されたExecutorService
をただちに実行するRunnable
は、次のように実装できます。
doAnswer(i -> {
((Runnable) i.getArguments()[0]).run();
return null;
}).when(executor).execute(any());
あなたの問題はあなたのテスト構造によるものだと思います。私がモックをテストクラスにインターフェースを実装する伝統的な方法と混ぜるのは難しいことがわかりました(ここでやったように)。
リスナーをモックとして実装した場合は、対話を検証できます。
Listener listener = mock(Listener.class);
w.addListener(listener);
world.doAction(..);
verify(listener).doAction();
これは、「世界」が正しいことをしているというあなたを満足させるはずです。
@ashley:私のために働く
public class AssetChangeListenerImpl extends AbstractAssetChangeListener implements AssetChangeListener { @Override public void onChangeEvent(final EventMessage message) throws EventHubClientException { execute(message); } } } public class AbstractAssetChangeListener { protected void execute( final EventMessage message ) throws EventHubClientException { executor.execute( new PublishTask(getClient(), message) ); } } @RunWith(MockitoJUnitRunner.class) public class AssetChangeListenerTest extends AbstractAssetChangeListenerTest { public void testExecute() throws EventHubClientException { EventMessage message = createEventMesage(EventType.CREATE); assetChangeListener.execute(message); verify(assetChangeListener, times(1)).execute(message); } }