1. ホーム
  2. java

Mockito、JUnitとSpring

2023-09-11 03:44:49

質問

私は今日、Mockitoについて学び始めました。私はいくつかの簡単なテスト(JUnitを使用して、以下を参照)を書きましたが、私はSpringの管理されたビーン内でモックオブジェクトをどのように使用できるかが分かりません。というのは ベストプラクティス は何ですか?モック化された依存関係をどのように自分のBeanに注入すればよいですか?

まで省略可能です。 私の質問に戻って .

まず、私が学んだこと。 これは非常に良い記事です モックはスタブではない という記事で、基本的なことが説明されています(Mockのチェックは 動作検証 ではなく 状態検証 ). それから、ここに良い例があります モッキート とこちら mockitoでより簡単にモック化 . Mockitoのモックオブジェクトが、いずれも モック スタブ .

ここで モッキート そして、ここでは マッチャー で、より多くの例を見ることができます。

このテストは

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

はうまく動作します。

私の質問に戻ります。 ここで MockitoのモックをSpringビーンに注入する 誰かがSpringを使おうとして 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

EDIT : 私は本当に明確ではなかった。私は私の自己を明確にするためにコードの3つの例を提供します。 私たちはメソッドを持つビーンHelloWorldを持っているとします。 printHello() とメソッドを持つビーンHelloFacadeがあるとします。 sayHello で、HelloWorld のメソッドへの呼び出しを転送します。 printHello() .

最初の例は、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();
}

}

前の例では、HelloFacade はインターフェースなので、手動で HelloFacadeImpl をインスタンス化し、HelloFacade に代入しなければなりませんでしたが、この例では、HelloFacadeImpl を宣言するだけで、木人がインスタンス化してくれます。この例では、HelloFacadeImpl を宣言するだけで、Mokito がそれをインスタンス化してくれます。しかし、この方法の欠点は、ユニット・アンダー・テストがインターフェイスではなく、impl-classになってしまうことです。

どうすればいいのか?

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

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

あなたのオリジナルの提案された方法、および例2、3は、まさに私が上記で説明したことを行っています。

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

1) SUTをSpringアプリコンテキストに作成し、それへの参照を取得し、モックを注入する。

@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の統合テスト、モックオブジェクトの作成 . この方法は、Springのアプリコンテキストでモックを作成し、アプリCTXからモックオブジェクトを取得してスタブや検証を行う方法です。

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

    @Autowired
    TestTarget sut;

    @Autowired
    Foo mockFoo;

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

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

補足ですが、@Mock、@Inject、MocktoJunitRunnerなどは、Mockitoを使う上では不要なものばかりです。 これらは、Mockito.mock(Foo.class)とセッターの起動を省くためのユーティリティに過ぎません。