モックオブジェクトの初期化-MockIto


122

MockItoを使用してモックオブジェクトを初期化する方法はたくさんあります。これらの中で最良の方法は何ですか?

1。

 public class SampleBaseTestCase {

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

2。

@RunWith(MockitoJUnitRunner.class)

[編集] 3。

mock(XXX.class);

これらより良い他の方法があるかどうか私に提案してください...

回答:


153

モックの初期化では、ランナーまたはの使用MockitoAnnotations.initMocksは完全に同等のソリューションです。MockitoJUnitRunnerのjavadocから:

JUnit 4.5 runner initializes mocks annotated with Mock, so that explicit usage of MockitoAnnotations.initMocks(Object) is not necessary. Mocks are initialized before each test method.


最初のソリューション()は、テストケースでMockitoAnnotations.initMocks特定のランナー(SpringJUnit4ClassRunnerたとえば)をすでに構成している場合に使用できます。

2番目のソリューション(を使用MockitoJUnitRunner)は、よりクラシックで私のお気に入りです。コードはより単純です。ランナーを使用すると、フレームワークの使用自動的に検証するという大きな利点がありますこの回答で@David Wallaceが説明しています)。

どちらのソリューションでも、テストメソッド間でモック(およびスパイ)を共有できます。と組み合わせると、@InjectMocksユニットテストを非常に迅速に記述できます。ボイラープレートのモッキングコードが削減され、テストが読みやすくなります。例えば:

@RunWith(MockitoJUnitRunner.class)
public class ArticleManagerTest {

    @Mock private ArticleCalculator calculator;
    @Mock(name = "database") private ArticleDatabase dbMock;
    @Spy private UserProvider userProvider = new ConsumerUserProvider();

    @InjectMocks private ArticleManager manager;

    @Test public void shouldDoSomething() {
        manager.initiateArticle();
        verify(database).addListener(any(ArticleListener.class));
    }

    @Test public void shouldDoSomethingElse() {
        manager.finishArticle();
        verify(database).removeListener(any(ArticleListener.class));
    }
}

長所:コードは最小限です

短所:黒魔術。IMOこれは主に@InjectMocksアノテーションが原因です。この注釈を使用すると、 「コードの苦痛を緩和できます」@Briceのすばらしいコメントを参照


3番目のソリューションは、各テストメソッドでモックを作成することです。@mlkの回答で説明されているように、「自己完結型テスト」を持つことができます。

public class ArticleManagerTest {

    @Test public void shouldDoSomething() {
        // given
        ArticleCalculator calculator = mock(ArticleCalculator.class);
        ArticleDatabase database = mock(ArticleDatabase.class);
        UserProvider userProvider = spy(new ConsumerUserProvider());
        ArticleManager manager = new ArticleManager(calculator, 
                                                    userProvider, 
                                                    database);

        // when 
        manager.initiateArticle();

        // then 
        verify(database).addListener(any(ArticleListener.class));
    }

    @Test public void shouldDoSomethingElse() {
        // given
        ArticleCalculator calculator = mock(ArticleCalculator.class);
        ArticleDatabase database = mock(ArticleDatabase.class);
        UserProvider userProvider = spy(new ConsumerUserProvider());
        ArticleManager manager = new ArticleManager(calculator, 
                                                    userProvider, 
                                                    database);

        // when 
        manager.finishArticle();

        // then
        verify(database).removeListener(any(ArticleListener.class));
    }
}

長所:APIの仕組みを明確に示す(BDD ...)

短所:ボイラープレートコードがさらにあります。(モック作成)


私の推奨は妥協です。で@Mockアノテーションを@RunWith(MockitoJUnitRunner.class)使用しますが、@InjectMocks:は使用しないでください

@RunWith(MockitoJUnitRunner.class)
public class ArticleManagerTest {

    @Mock private ArticleCalculator calculator;
    @Mock private ArticleDatabase database;
    @Spy private UserProvider userProvider = new ConsumerUserProvider();

    @Test public void shouldDoSomething() {
        // given
        ArticleManager manager = new ArticleManager(calculator, 
                                                    userProvider, 
                                                    database);

        // when 
        manager.initiateArticle();

        // then 
        verify(database).addListener(any(ArticleListener.class));
    }

    @Test public void shouldDoSomethingElse() {
        // given
        ArticleManager manager = new ArticleManager(calculator, 
                                                    userProvider, 
                                                    database);

        // when 
        manager.finishArticle();

        // then 
        verify(database).removeListener(any(ArticleListener.class));
    }
}

長所:APIがどのように機能するかを明確に示します(ArticleManagerインスタンス化の方法)。定型コードはありません。

短所:テストは自己完結型ではないため、コードの苦痛が少ない


ただし、注釈は有用ですが、不十分なOO設計を作成する(またはそれを低下させる)ことから保護しません。個人的にはボイラープレートコードを減らして満足していますが、デザインをより良いものに変更するきっかけとなるコード(またはPITA)の痛みを和らげ、私とチームはOOデザインに注意を払っています。SOLID設計やGOOSのアイデアのような原則に従ってOO設計に従うことは、モックをインスタンス化する方法を選択することよりもはるかに重要であると感じています。
Brice、2013年

1
(フォローアップ)このオブジェクトがどのように作成されるのかわからない場合は、それについて苦痛を感じることはありません。また、新しい機能を追加する必要がある場合、将来のプログラマーはうまく反応しない可能性があります。どちらにしても議論の余地がありますが、私はそれについて注意するように言っています。
Brice

6
これら2つが同等であることは正しくありません。より単純なコードがを使用する唯一の利点であるというのは真実ではありませんMockitoJUnitRunner。違いの詳細については、stackoverflow.com / questions / 10806345 / の質問とその回答を参照してください。
Dawood ibnカリーム2013年

2
@Gontard確かに依存関係は表示されていますが、このアプローチを使用するとコードが間違っているのを見てきました。の使用についてCollaborator collab = mock(Collaborator.class)、私の意見では、この方法は確かに有効なアプローチです。これは冗長になりがちですが、テストの理解とリファクタリングを得ることができます。どちらの方法にも長所と短所があり、どちらの方法が優れているかはまだわかりません。Amywayは常にがらくたを書くことが可能であり、おそらくコンテキストとコーダーに依存します。
Brice、2013年

1
@mlk私はあなたに完全に同意します。私の英語はあまり上手ではなく、ニュアンスに欠けています。私のポイントは、UNITという言葉を主張することでした。
gontard 2013年

30

MockitoRule と呼ばれるJUnit4 ルールを使用してモックをインスタンス化する4番目の方法が(v1.10.7以降)あります。

@RunWith(JUnit4.class)   // or a different runner of your choice
public class YourTest
  @Rule public MockitoRule rule = MockitoJUnit.rule();
  @Mock public YourMock yourMock;

  @Test public void yourTestMethod() { /* ... */ }
}

JUnit は@Ruleで注釈が付けられたTestRuleのサブクラスを探し、それらを使用して、Runnerが提供するテストステートメントラップします。その結果、@ Beforeメソッド、@ Afterメソッドを抽出し、さらに...ラッパーをルールにキャッチすることができます。ExpectedExceptionが行うように、テスト内からこれらと対話することもできます。

MockitoRuleの動作はMockitoJUnitRunnerとほぼ同じですが、Parameterized(テストコンストラクターが引数を取得してテストを複数回実行できるようにする)や、Robolectricのテストランナー(クラスローダーがJavaの置換を提供できるなど)を使用できる点が異なります。 Androidネイティブクラスの場合)。これにより、最近のJUnitおよびMockitoバージョンでの使用が厳密に柔軟になります。

要約すれば:

  • Mockito.mock():アノテーションのサポートや使用法の検証なしの直接呼び出し。
  • MockitoAnnotations.initMocks(this):注釈サポート、使用法検証なし。
  • MockitoJUnitRunner:注釈のサポートと使用法の検証。ただし、そのランナーを使用する必要があります。
  • MockitoRule:JUnitランナーでの注釈サポートと使用法検証。

参照:JUnit @Ruleの仕組み


3
Kotlinでは、ルールは次のようになります@get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
Cristan

10

これを行うには、きちんとした方法があります。

  • 単体テストの場合、これを行うことができます:

    @RunWith(MockitoJUnitRunner.class)
    public class MyUnitTest {
    
        @Mock
        private MyFirstMock myFirstMock;
    
        @Mock
        private MySecondMock mySecondMock;
    
        @Spy
        private MySpiedClass mySpiedClass = new MySpiedClass();
    
        // It's gonna inject the 2 mocks and the spied object per reflection to this object
        // The java doc of @InjectMocks explains it really well how and when it does the injection
        @InjectMocks
        private MyClassToTest myClassToTest;
    
        @Test
        public void testSomething() {
        }
    }
  • 編集:それが統合テストの場合はこれを行うことができます(Springでそのように使用することを意図していません。異なるランナーでモックを初期化できることを示すだけです):

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("aplicationContext.xml")
    public class MyIntegrationTest {
    
        @Mock
        private MyFirstMock myFirstMock;
    
        @Mock
        private MySecondMock mySecondMock;
    
        @Spy
        private MySpiedClass mySpiedClass = new MySpiedClass();
    
        // It's gonna inject the 2 mocks and the spied object per reflection to this object
        // The java doc of @InjectMocks explains it really well how and when it does the injection
        @InjectMocks
        private MyClassToTest myClassToTest;
    
        @Before
        public void setUp() throws Exception {
              MockitoAnnotations.initMocks(this);
        }
    
        @Test
        public void testSomething() {
        }
    }

1
MOCKが統合テストにも関与している場合、それは理にかなっていますか?
VinayVeluri 2013年

2
実際にはそうではありません、あなたの権利。私はモッキートの可能性を示したかっただけです。たとえばRESTFuseを使用する場合は、MockitoAnnotations.initMocks(this);でモックを初期化できるように、ランナーを使用する必要があります。
emd '19年

8

MockitoAnnotationsとランナーについては上記で十分に説明しているので、愛していない人のためにタッペンを投入します。

XXX mockedXxx = mock(XXX.class);

私がこれを使用するのは、それがもう少し説明的であると思うので、私は自分のテストを(できるだけ)自己完結させたいので、メンバー変数を使用しない(適切な禁止ではない)単体テストを好むからです。


テストケースを自己完結型にすることを除いて、mock(XX.class)を使用することに他の利点はありますか?
VinayVeluri 2013年

私の知る限りではありません。
マイケルロイドリーmlk 2013年

3
テストを読むために理解する必要のある魔法が少なくなります。変数を宣言し、値を付けます-注釈、リフレクションなどはありません
Karu

2

JUnit 5 Jupiterの小さな例である「RunWith」が削除され、「@ ExtendWith」アノテーションを使用して拡張機能を使用する必要があります。

@ExtendWith(MockitoExtension.class)
class FooTest {

  @InjectMocks
  ClassUnderTest test = new ClassUnderTest();

  @Spy
  SomeInject bla = new SomeInject();
}

0

他の回答はすばらしいです。必要に応じて、詳細を記載してください。
これらに加えて、TL; DRを追加したいと思います。

  1. 使用することを好む
    • @RunWith(MockitoJUnitRunner.class)
  2. (すでに別のランナーを使用しているため)できない場合は、
    • @Rule public MockitoRule rule = MockitoJUnit.rule();
  3. (2)と同様に、しかし、あなたはすべきではないもうこれを使用します。
    • @Before public void initMocks() { MockitoAnnotations.initMocks(this); }
  4. テストの1つだけでモックを使用し、同じテストクラスの他のテストにモックを公開したくない場合は、
    • X x = mock(X.class)

(1)と(2)と(3)は相互に排他的です。
(4)他と組み合わせて使用​​できます。

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