web-dev-qa-db-ja.com

Mockito:メソッド内で作成されたオブジェクトでメソッドが呼び出されたことを確認する方法

私はMockitoが初めてです。

以下のクラスで、someMethodが呼び出された後にfooが一度だけ呼び出されたことをMockitoを使って確認するにはどうすればよいですか?

public class Foo
{
    public void foo(){
        Bar bar = new Bar();
        bar.someMethod();
    }
}

次の確認電話をかけたいです。

verify(bar, times(1)).someMethod();

ここでbarBarの擬似インスタンスです。

253
mre

依存性注入

Barインスタンス、またはBarインスタンスを作成するために使用されるファクトリ(またはこれを行う他の483の方法のうちの1つ)を注入すると、テストを実行するために必要なアクセス権が得られます。

工場の例:

このように書かれたFooクラスを考えます。

public class Foo {
  private BarFactory barFactory;

  public Foo(BarFactory factory) {
    this.barFactory = factory;
  }

  public void foo() {
    Bar bar = this.barFactory.createBar();
    bar.someMethod();
  }
}

あなたのテストメソッドでは、このようにBarFactoryを注入することができます:

@Test
public void testDoFoo() {
  Bar bar = mock(Bar.class);
  BarFactory myFactory = new BarFactory() {
    public Bar createBar() { return bar;}
  };

  Foo foo = new Foo(myFactory);
  foo.foo();

  verify(bar, times(1)).someMethod();
}

おまけ:これは、TDDがコードの設計をどのように推進できるかの例です。

287
csturtz

古典的な反応は、「あなたはしない」です。内部ではなくFooのパブリックAPIをテストします。

foo()の影響を受けているFooオブジェクト(あるいは環境内の他のオブジェクト)の動作はありますか?もしそうなら、それをテストします。そうでなければ、メソッドは何をしますか?

17

DIや工場を使いたくない場合あなたは少しトリッキーな方法であなたのクラスをリファクタリングすることができます:

public class Foo {
    private Bar bar;

    public void foo(Bar bar){
        this.bar = (bar != null) ? bar : new Bar();
        bar.someMethod();
        this.bar = null;  // for simulating local scope
    }
}

そしてあなたのテストクラス:

@RunWith(MockitoJUnitRunner.class)
public class FooTest {
    @Mock Bar barMock;
    Foo foo;

    @Test
    public void testFoo() {
       foo = new Foo();
       foo.foo(barMock);
       verify(barMock, times(1)).someMethod();
    }
}

それからあなたのfooメソッドを呼んでいるクラスはこのようにするでしょう:

public class thirdClass {

   public void someOtherMethod() {
      Foo myFoo = new Foo();
      myFoo.foo(null);
   }
}

このようにメソッドを呼び出すときにわかるように、Barクラスをfooメソッドを呼び出している他のクラスにインポートする必要はありません。

もちろん、欠点は、呼び出し側にBarオブジェクトの設定を許可していることです。

それが役に立てば幸い。

12
raspacorp

PowerMockito.whenNewを使用したサンプルコードの解決策

  • モッキートオール1.10.8
  • パワーモックコア1.6.1
  • powermock-module-junit4 1.6.1
  • powermock-api-mockito 1.6.1
  • ジュニット4.12

FooTest.Java

package foo;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

//Both @PrepareForTest and @RunWith are needed for `whenNew` to work 
@RunWith(PowerMockRunner.class)
@PrepareForTest({ Foo.class })
public class FooTest {

    // Class Under Test
    Foo cut;

    @Mock
    Bar barMock;

    @Before
    public void setUp() throws Exception {
        cut = new Foo();

    }

    @After
    public void tearDown() {
        cut = null;

    }

    @Test
    public void testFoo() throws Exception {

        // Setup
        PowerMockito.whenNew(Bar.class).withNoArguments()
                .thenReturn(this.barMock);

        // Test
        cut.foo();

        // Validations
        Mockito.verify(this.barMock, Mockito.times(1)).someMethod();

    }

}

JUnit出力 JUnit Output

8
javaPlease42

私はMockitoの@InjectMocksが行くべき道だと思います。

あなたの意図に応じて、あなたは使うことができます:

  1. コンストラクタインジェクション
  2. プロパティセッターインジェクション
  3. フィールドインジェクション

docs の詳細情報

以下はフィールドインジェクションの例です。

クラス:

public class Foo
{
    private Bar bar = new Bar();

    public void foo() 
    {
        bar.someMethod();
    }
}

public class Bar
{
    public void someMethod()
    {
         //something
    }
}

テスト:

@RunWith(MockitoJUnitRunner.class)
public class FooTest
{
    @Mock
    Bar bar;

    @InjectMocks
    Foo foo;

    @Test
    public void FooTest()
    {
        doNothing().when( bar ).someMethod();
        foo.foo();
        verify(bar, times(1)).someMethod();
    }
}
7
siulkilulki

はい、本当にやりたい、または必要な場合は、PowerMockを使用できます。これは最後の手段と考えるべきです。 PowerMockを使うと、呼び出しからコンストラクターへのモックを返させることができます。その後、モックで確認をしてください。とは言っても、csturtzが「正しい」答えです。

これは へのリンクです - 新しいオブジェクトのモック構築

3
John B

別の簡単な方法は、bar.someMethod()にログステートメントを追加し、テストの実行時に上記のメッセージが表示されることを確認することです。こちらの例を参照してください: ロガー内のメッセージ

これは、Bar.someMethod()がprivateの場合に特に便利です。

0
Nestor Milyaev