Mockitoでvoidメソッドをモックする方法


938

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();
}

システムはモックでトリガーされません。

上記のシステム状態を示したい。そして、それらに応じて主張をします。


6
モックのvoidメソッドはデフォルトでは何もしないことに注意してください!
ラインの

1
@Line、それは私が探していたものです。言ってみれば当たり前のようです。ただし、モックの原則が強調されています。モックされたクラスのメソッドをモックする必要があるのは、戻り値や例外などの効果だけです。ありがとうございました!
allenjom

回答:


1145

Mockito APIのドキュメントをご覧ください。リンクされたドキュメントとして、あなたは、任意の使用することができます(ポイント#12)を言及しdoThrow()doAnswer()doNothing()doReturn()モックのボイド方法にMockitoフレームワークからのメソッドの家族を。

例えば、

Mockito.doThrow(new Exception()).when(instance).methodName();

それをフォローアップ行動と組み合わせたい場合は、

Mockito.doThrow(new Exception()).doNothing().when(instance).methodName();

setState(String s)以下のクラスWorldのセッター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());

8
@qualidafial:ええ、私は戻り値の型に興味がないことをよりよく伝えるので、Voidへのパラメーター化はより良いと思います。指摘してくれてありがとう、私はこの構造に気づきませんでした。
sateesh 2010

2
doThrowが#5になりました(doThrowを使用している場合も、これにより、「 'void'タイプはここでは許可されません」というメッセージがフォロワーに表示されました...)
rogerdpack

@qualidafial:Answer.answer呼び出しの戻り値の型は、元のメソッドに返されるものではなく、doAnswer呼び出しに返されるものだと思います。おそらく、テストでその値を使用して何か他のことをしたい場合です。
twelve17 '

1
:( guavaのバージョン16.0.1のRateLimiter.javaをモックしようとすると、doNothing()。when(mockLimiterReject).setRate(100)の結果、RateLimiterのsetRateが呼び出され、モックトが何らかの理由でバイトコード化されると何らかの理由でミューテックスがnullになるため、nullpointerが発生します。そのため、私のsetRateメソッド:(をモックしませんでしたが、代わりにそれを呼び出しました:(
Dean Hiller

2
@DeanHiller setRate()finalであり、したがってモックすることはできません。代わりにcreate()、必要なことを行うインスタンスを試してください。モックする必要はないはずRateLimiterです。
dimo414

113

私はその質問に対するより簡単な答えを見つけたと思います、実際のメソッドを1つのメソッドだけで呼び出すために(それがvoidの戻り値を持っている場合でも)、これを行うことができます:

Mockito.doCallRealMethod().when(<objectInstance>).<method>();
<objectInstance>.<method>();

または、そのクラスのすべてのメソッドの実際のメソッドを呼び出して、次のようにすることもできます。

<Object> <objectInstance> = mock(<Object>.class, Mockito.CALLS_REAL_METHODS);

13
これが本当の答えです。spy()メソッドは正常に動作しますが、通常、オブジェクトがほとんどすべてのことを通常どおりに実行するようにするために予約されています。
biggusjimmus 14

1
これは何を意味するのでしょうか?実際にメソッドを呼び出していますか?これまでモッキートを使ったことがありません。
obesechicken13 2015年

はい、モックは実際のメソッドを呼び出します。@Mockを使用する場合は、@ Mock(answer = Answers.CALLS_REAL_METHODS)で同じように指定して、同じ結果を得ることができます。
Ale

70

@sateeshの発言に加えて、テストがそれを呼び出さないようにするためにvoidメソッドをモックしたい場合は、次の方法を使用できますSpy

World world = new World();
World spy = Mockito.spy(world);
Mockito.doNothing().when(spy).methodToMock();

テストを実行する場合は、spyオブジェクトではなくオブジェクトでtestのメソッドを呼び出してくださいworld。例えば:

assertEquals(0,spy.methodToTestThatShouldReturnZero());

57

いわゆる問題の解決策は、Mockito.mock(..)の代わりにspy Mockito.spy(...)を使用することmock です

Spyは部分的なモックを可能にします。Mockitoはこの問題に長けています。完成していないクラスがあるので、このようにして、このクラスの必要な場所を模擬します。


3
私は同様の問題を抱えていたので、ここで偶然偶然に遭遇しました(偶然にも、偶然にもサブジェクト/オブザーバーの相互作用をテストしている)。私はすでにスパイを使用していますが、「SubjectChanged」メソッドで別のことをしたいと思っています。メソッドが呼び出されたことを確認するためだけに、 `verify(observer).subjectChanged(subject)を使用できます。しかし、何らかの理由で、私はむしろメソッドをオーバーライドしたいと思います。そのため、Sateeshのアプローチとここでのあなたの答えの組み合わせが、進むべき道でした...
gMale

32
いいえ、これを行っても、実際にはvoidメソッドをモックするのに役立ちません。コツは、sateeshの回答にリストされている4つのMockito静的メソッドの1つを使用することです。
Dawood ibnカリーム2013年

2
@Gurnardの質問は、このstackoverflow.com/questions/1087339/…をご覧ください。
ibrahimyilmaz 2013年

この用量は実際に機能します。
Nilotpal

34

まず最初に、常にmockito staticをインポートする必要があります。これにより、コードがより読みやすく(直感的に)なります。

import static org.mockito.Mockito.*;

部分的なモッキングのために、残りの部分で元の機能を維持するために、mockitoは「スパイ」を提供します。

次のように使用できます。

private World world = spy(World.class);

メソッドが実行されないようにするには、次のようなものを使用できます。

doNothing().when(someObject).someMethod(anyObject());

メソッドにいくつかのカスタム動作を与えるには、「thenReturn」で「when」を使用します。

doReturn("something").when(this.world).someMethod(anyObject());

他の例については、ドキュメントで優れたmockitoサンプルを見つけてください。


4
静的インポートは読みやすくするために何をする必要がありますか?
jsonbourne 2018

2
文字通り何もない。
Specializ

2
それは好みの問題だと思います。ステートメントを(ほとんど)英語の文のように見せたいだけです。Class.methodname()。something()とmethodname()の違いです。
fl0w 2018

1
チームで作業している場合、import staticを使用すると、ワイルドカードがしばしばインポートされるため、import staticを使用すると、メソッドがどこから来ているのかを他の人が見るのが難しくなります。
LowKeyEnergy

これは正しいですが、テストコードとMockitoの場合、これは明白で問題ではありません。
fl0w

27

mockitoでvoidメソッドをモックする方法-2つのオプションがあります。

  1. doAnswer -モックされたvoidメソッドで何かを実行したい場合(ボイドであっても動作をモックする)。
  2. doThrow-次にMockito.doThrow()、モックされたvoidメソッドから例外をスローしたい場合があります。

以下は、それを使用する方法の例です(理想的なユースケースではなく、基本的な使用法を説明したいだけです)。

@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("old@test.com", "new@test.com");

    //some asserts
    assertThat(customer, is(notNullValue()));
    assertThat(customer.getEmail(), is(equalTo("new@test.com")));

}

@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("old@test.com", "new@test.com");

}
}

Mockitoでvoidメソッドをモックしてテスト する方法の詳細については、私の投稿を参照してください。Mockitoでモックする方法(例を含む包括的なガイド)


1
素晴らしい例。注:Java 8では、無名クラスの代わりにラムダを使用する方が少し良いかもしれません: 'doAnswer((Answer <Void>)invocation-> {// CODE})。when(mockInstance).add(method ()); '
miwe

16

束に別の答えを追加しています(しゃれは意図されていません)...

スパイを使用したくない場合は、doAnswerメソッドを呼び出す必要があります。ただし、必ずしも独自のAnswerをロールする必要はありません。いくつかのデフォルトの実装があります。特に、CallsRealMethods

実際には、次のようになります。

doAnswer(new CallsRealMethods()).when(mock)
        .voidMethod(any(SomeParamClass.class));

または:

doAnswer(Answers.CALLS_REAL_METHODS.get()).when(mock)
        .voidMethod(any(SomeParamClass.class));

16

Java 8では、以下の静的インポートがあると仮定して、これを少しクリーンにすることができますorg.mockito.Mockito.doAnswer

doAnswer(i -> {
  // Do stuff with i.getArguments() here
  return null;
}).when(*mock*).*method*(*methodArguments*);

これreturn null;は重要であり、これがないと、の適切なオーバーライドを見つけることができないため、コンパイルはかなり不明瞭なエラーで失敗しますdoAnswer

たとえばExecutorServiceRunnable渡されたものをすぐに実行するは、次execute()を使用して実装できます。

doAnswer(i -> {
  ((Runnable) i.getArguments()[0]).run();
  return null;
}).when(executor).execute(any());

2
1行で:Mockito.doAnswer((i)-> null).when(instance).method(any());
Akshay Thorve

@AkshayThorve私が実際に何かをしたいときはそれはうまくいきません。
Tim B

7

私はあなたの問題はあなたのテスト構造に起因すると思います。ここで行ったように、モックとテストクラスにインターフェースを実装する従来の方法を混在させるのは難しいと思いました。

リスナーをモックとして実装すると、相互作用を確認できます。

Listener listener = mock(Listener.class);
w.addListener(listener);
world.doAction(..);
verify(listener).doAction();

これは、「世界」が正しいことをしていることを満足させるはずです。


0

次のようにMockito.doThrowを使用します。

Mockito.doThrow(new Exception()).when(instance).methodName();

あなたはこの素晴らしい例を試すことができます:

public void testCloseStreamOnException() throws Exception {
    OutputStream outputStream = Mockito.mock(OutputStream.class);
    IFileOutputStream ifos = new IFileOutputStream(outputStream);
    Mockito.doThrow(new IOException("Dummy Exception")).when(outputStream).flush();
    try {
      ifos.close();
      fail("IOException is not thrown");
    } catch (IOException ioe) {
      assertEquals("Dummy Exception", ioe.getMessage());
    }
    Mockito.verify(outputStream).close();
  }

出典:http : //apisonar.com/java-examples/org.mockito.Mockito.doThrow.html#Example-19

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.