web-dev-qa-db-ja.com

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

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

1。

 public class SampleBaseTestCase {

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

2。

@RunWith(MockitoJUnitRunner.class)

[編集] 3。

mock(XXX.class);

これらよりも良い方法が他にあるなら私に提案してください...

97
VinayVeluri

モックの初期化では、ランナーまたは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 = in この回答 )。

どちらのソリューションでも、テストメソッド間でモック(およびスパイ)を共有できます。 @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 で説明されているように、その答えに「self Contained Test」を含めることができます。

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 ...)

短所:定型コードがさらにあります。 (モック作成)


My戒めは妥協です。 @RunWith(MockitoJUnitRunner.class)@Mock注釈を使用しますが、@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のインスタンス化方法)を明確に示します。定型コードはありません。

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

124
gontard

(v1.10.7時点で)モックをインスタンス化する4番目の方法があります。これは MockitoRule と呼ばれるJUnit4ruleを使用しています。 。

@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のサブクラス を探し、それらを使用してランナーが提供するテストステートメントをラップします。これの結果は、@ Beforeメソッド、@ Afterメソッド、さらにはtry ... catchラッパーをルールに抽出できることです。 ExpectedException のように、テスト内からこれらと対話することもできます。

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

要約すれば:

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

参照: JUnit @Ruleの仕組み

24
Jeff Bowman

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

  • 単体テストの場合、これを実行できます。

    @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() {
        }
    }
    
9
emd

MockitoAnnotationsとランナーについては上で詳しく説明しました。

XXX mockedXxx = mock(XXX.class);

私はこれをもう少し説明的であると思うので、これを使用します(私はテストが自己完結的であることが可能な限り)メンバー変数を使用しないように(正しい禁止ではなく)単体テストを好むのです。

他の回答は素晴らしく、必要に応じて詳細が含まれています。
これらに加えて、TL; DRを追加します。

  1. 使用することを好む
    • @RunWith(MockitoJUnitRunner.class)
  2. (すでに別のランナーを使用しているため)できない場合は、を使用することをお勧めします
    • @Rule public MockitoRule rule = MockitoJUnit.rule();
  3. (2)に似ていますが、not使用する必要があります:
    • @Before public void initMocks() { MockitoAnnotations.initMocks(this); }
  4. 1つのテストでのみモックを使用し、同じテストクラス内の他のテストに公開したくない場合は、use を使用します。
    • X x = mock(X.class)

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

0
neXus