79

私は今日だけMockitoについて学び始めました。簡単なテストを作成しましたが(JUnitを使用、以下を参照)、SpringのマネージドBean内でモックオブジェクトを使用する方法がわかりません。Springを使用するためのベストプラクティスは何ですか。Beanにモックされた依存関係をどのように注入する必要がありますか?

私の質問に戻るまでこれをスキップできます。

まず第一に、私が学んだこと。これは、基本を説明する非常に優れた記事Mocks Are n't Stubsです(Mockは、状態の検証ではなく、動作の検証をチェックします)。次に、ここにMockito と、ここにmockitoを使用したより簡単なモックの良い例があります。Mockitoのモックオブジェクトはモックスタブの両方であるという説明があります。

ここMockitoとここMatchers、あなたはより多くの例を見つけることができます。

このテスト

@Test
public void testReal(){
    List<String> mockedList = mock(List.class);
     //stubbing
     //when(mockedList.get(0)).thenReturn("first");

    mockedList.get(anyInt());
    OngoingStubbing<String> stub= when(null);
    stub.thenReturn("first");

    //String res = mockedList.get(0);
                //System.out.println(res);

     //you can also verify using argument matcher
     //verify(mockedList).get(anyInt());

    verify(mockedList);
    mockedList.get(anyInt());
}

うまく動作します。

私の質問に戻ります。ここでは、誰かがSpringを使用しようとするSpring BeanにMockitoモックを注入ReflectionTestUtils.setField()しますが、ここでは、Spring統合テスト、モックオブジェクトの作成で、 Springのコンテキストを変更することをお勧めします。

最後の2つのリンクがよくわかりませんでした...SpringがMockitoでどのような問題を抱えているのか、誰かに説明してもらえますか?このソリューションの何が問題になっていますか?

@InjectMocks
private MyTestObject testObject

@Mock
private MyDependentObject mockedObject

@Before
public void setup() {
        MockitoAnnotations.initMocks(this);
}

https://stackoverflow.com/a/8742745/1137529

編集:私は本当に明確ではありませんでした。自分自身を明確にするために、コードの3つの例を示します。HelloWorldのメソッドへの呼び出しを転送するメソッドをprintHello()持つBeanHelloWorldとメソッドを持つBeanHelloFacadeがあるとします。sayHelloprintHello()

最初の例は、Springのコンテキストを使用し、カスタムランナーを使用せずに、依存性注入(DI)にReflectionTestUtilsを使用することです。

public class Hello1Test  {
private ApplicationContext ctx;

@Before
public void setUp() {
    MockitoAnnotations.initMocks(this);
    this.ctx = new ClassPathXmlApplicationContext("META-INF/spring/ServicesImplContext.xml");
}



@Test
public void testHelloFacade() {
    HelloFacade obj = (HelloFacade) ctx.getBean(HelloFacadeImpl.class);
    HelloWorld mock = mock(HelloWorld.class);
    doNothing().when(mock).printHello();

    ReflectionTestUtils.setField(obj, "hello", mock);
    obj.sayHello();

    verify(mock, times(1)).printHello();
}

}

@Noamが指摘したように、を明示的に呼び出さずに実行する方法がありますMockitoAnnotations.initMocks(this);。また、この例ではSpringのコンテキストの使用をやめます。

@RunWith(MockitoJUnitRunner.class)
public class Hello1aTest {


@InjectMocks
private HelloFacade obj =  new HelloFacadeImpl();

@Mock
private HelloWorld mock;


@Test
public void testHelloFacade() {
    doNothing().when(mock).printHello();
    obj.sayHello();
}

}

これを行う別の方法

public class Hello1aTest {

@Before
public void setUp() {
    MockitoAnnotations.initMocks(this);
}


@InjectMocks
private HelloFacadeImpl obj;

@Mock
private HelloWorld mock;


@Test
public void testHelloFacade() {
    doNothing().when(mock).printHello();
    obj.sayHello();
}

}

いいえ、前の例では、HelloFacadeImplを手動でインスタンス化して、HelloFacadeに割り当てる必要があります。これは、HelloFacadeがインターフェイスであるためです。最後の例では、HelloFacadeImplを宣言するだけで、Mokitoがそれをインスタンス化します。このアプローチの欠点は、現在、テスト対象のユニットがimplクラスであり、インターフェイスではないことです。

4

7 に答える 7

55

正直なところ、私があなたの質問を本当に理解しているかどうかはわかりません:PIは、あなたの元の質問から得たものから、私ができる限り明確にしようとします:

まず、ほとんどの場合、Springについて心配する必要はありません。ユニットテストの作成にSpringを関与させる必要はめったにありません。通常の場合、単体テストでテスト対象システム(SUT、テスト対象)をインスタンス化するだけでよく、テストにもSUTの依存性を注入する必要があります。依存関係は通常、モック/スタブです。

あなたの最初の提案された方法、そして例2、3は、私が上で説明したことを正確に行っています。

まれなケース(統合テストや特別な単体テストなど)では、Springアプリコンテキストを作成し、アプリコンテキストからSUTを取得する必要があります。そのような場合、私はあなたができると信じています:

1)春のアプリctxでSUTを作成し、それを参照して、モックを注入します

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("test-app-ctx.xml")
public class FooTest {

    @Autowired
    @InjectMocks
    TestTarget sut;

    @Mock
    Foo mockFoo;

    @Before
    /* Initialized mocks */
    public void setup() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void someTest() {
         // ....
    }
}

また

2)リンクSpring Integration Tests、CreatingMockObjectsで説明されている方法に従います。このアプローチは、Springのアプリコンテキストでモックを作成することであり、アプリctxからモックオブジェクトを取得してスタブ/検証を行うことができます。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("test-app-ctx.xml")
public class FooTest {

    @Autowired
    TestTarget sut;

    @Autowired
    Foo mockFoo;

    @Test
    public void someTest() {
         // ....
    }
}

どちらの方法でも機能するはずです。主な違いは、前者の場合は春のライフサイクルなど(Beanの初期化など)を経た後に依存関係が注入されるのに対し、後者の場合は事前に注入されることです。たとえば、SUTがSpringのInitializingBeanを実装していて、初期化ルーチンに依存関係が含まれている場合、これら2つのアプローチの違いがわかります。自分が何をしているのかを知っている限り、これら2つのアプローチには正しいことも悪いこともないと思います。

Mockitoを使用する場合、補足、@ Mock、@ Inject、MocktoJunitRunnerなどはすべて不要です。これらは、Mockito.mock(Foo.class)と一連のセッター呼び出しを入力する手間を省くための単なるユーティリティです。

于 2012-06-07T02:22:23.670 に答える
6

あなたの質問は、あなたが与えた3つの例のどれが好ましいアプローチであるかについて尋ねているようです。

Reflection TestUtilsを使用した例1は、単体テストには適していません。ユニットテストのためにSpringコンテキストをロードする必要はまったくありません。他の例に示されているように、必要なものをモックして注入するだけです。

統合テストを実行する場合は、Springコンテキストをロードする必要がありますが、明示的にBeanにアクセスする必要がある場合は、を使用@RunWith(SpringJUnit4ClassRunner.class)してコンテキストのロードを実行することをお勧めします。@Autowired

例2は有効なアプローチであり、を使用する@RunWith(MockitoJUnitRunner.class)と、@Beforeメソッドを指定する必要がなくなります。MockitoAnnotations.initMocks(this);

例3は、を使用しない別の有効なアプローチです@RunWith(...)。テスト対象のクラスをHelloFacadeImpl明示的にインスタンス化していませんが、例2で同じことを行うことができます。

私の提案は、コードの乱雑さを減らすために、単体テストに例2を使用することです。強制された場合は、より詳細な構成にフォールバックできます。

于 2012-06-07T00:12:34.987 に答える
4

Spring 4.2.RC1にいくつかの新しいテスト機能が導入されたことにより、に依存しないSpring統合テストを作成できるようになりましたSpringJUnit4ClassRunnerドキュメントのこの部分を確認してください。

あなたの場合、Spring統合テストを記述しても、次のようなモックを使用できます。

@RunWith(MockitoJUnitRunner.class)
@ContextConfiguration("test-app-ctx.xml")
public class FooTest {

    @ClassRule
    public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule();

    @Rule
    public final SpringMethodRule springMethodRule = new SpringMethodRule();

    @Autowired
    @InjectMocks
    TestTarget sut;

    @Mock
    Foo mockFoo;

    @Test
    public void someTest() {
         // ....
    }
}
于 2015-05-26T15:01:22.040 に答える
2

MockitoAnnotations.initMocks(this);mockito 1.9(またはそれ以降)を使用している場合は、実際には必要ありません。必要なのはこれだけです。

@InjectMocks
private MyTestObject testObject;

@Mock
private MyDependentObject mockedObject;

注釈は、すべてのモックをオブジェクト@InjectMocksに注入します。MyTestObject

于 2012-06-06T08:07:57.817 に答える
2

これが私の短い要約です。

単体テストを作成する場合は、単体テストを行うクラスに実際の依存関係を注入したくないため、SpringapplicationContextを使用しないでください。代わり@RunWith(MockitoJUnitRunner.class)に、クラスの先頭にアノテーションを付けるかMockitoAnnotations.initMocks(this)、@Beforeメソッドでモックを使用してください。

統合テストを作成する場合は、次を使用します。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("yourTestApplicationContext.xml")

たとえば、インメモリデータベースを使用してアプリケーションコンテキストを設定します。通常、統合テストではモックを使用しませんが、MockitoAnnotations.initMocks(this)上記のアプローチを使用してモックを使用できます。

于 2015-01-27T09:50:59.563 に答える
0

注釈付きフィールドをインスタンス化する必要があるかどうかの違いは@InjectMocks、MockitoJunitRunnerを使用するか、を使用するかではなく、MockitoのバージョンにありますMockitoAnnotations.initMocks。1.9では、フィールドのコンストラクター注入も処理@Mockし、インスタンス化を行います。以前のバージョンでは、自分でインスタンス化する必要があります。

これが私のSpringBeanのユニットテストを行う方法です。問題はない。Spring構成ファイルを使用して実際にモックのインジェクションを実行したい場合、人々は混乱に陥ります。これは、単体テストと統合テストのポイントを超えています。

そしてもちろん、テスト中のユニットはImplです。あなたは本当の具体的なことをテストする必要がありますよね?それをインターフェースとして宣言したとしても、それをテストするために本物をインスタンス化する必要があります。これで、実際のオブジェクトのスタブ/モックラッパーであるスパイに入ることができますが、それはコーナーケースの場合です。

于 2012-06-06T22:26:11.853 に答える
0

プロジェクトをSpringBoot1.4に移行する場合は@MockBean、偽造に新しいアノテーションを使用できますMyDependentObject@Mockこの機能を使用すると、Mockitoと@InjectMocksアノテーションをテストから 削除できます。

于 2016-09-05T13:30:07.740 に答える