2013-03-19 79 views
69

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

1.

public class SampleBaseTestCase { 

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

2.

@RunWith(MockitoJUnitRunner.class) 

[EDIT] 3.

mock(XXX.class); 

これらよりも良い他の方法があるなら、私を提案...

答えて

87

モックの初期化の場合、ランナーまたはMockitoAnnotations.initMocksを使用することは、厳密には同等のソリューションです。 MockitoJUnitRunnerのjavadocのから:あなたのテストケースで特定のランナー(例えばSpringJUnit4ClassRunner)をすでに設定しているとき

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付き)最初のソリューションを使用することができます。

2番目の解決策(MockitoJUnitRunner)は、より古典的で私の好きなものです。コードは簡単です。ランナーを使用することは、automatic validation of framework usage@David Wallaceで表され、this answerに記載されている)の大きな利点を提供します。

両方のソリューションでは、テストメソッド間でモック(およびスパイ)を共有できます。 @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の偉大なコメントを参照してください)


「あなたは、コードの痛みを失う」第三の解決策は、各テストメソッドであなたのモックを作成することです。 @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 ...)

短所を示します。より多くの定型コードがあります。 (モックの作成)


recommandationは妥協です。@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がインスタンス化された方法)方法を示します。ボイラープレートコードはありません。

短所:テストは自己完結型ではありません、コード

+0

は注意してくださいただし、注釈は便利ですが、彼らは貧しいOOデザインを作るために(またはそれを分解するために)に対してあなたを保護することはできません。個人的にはボイラープレートのコードを減らすことができて嬉しいですが、デザインをより良いものに変えるトリガーとなるコード(またはPITA)の痛みが緩んでいるので、私とチームはオブジェクト指向設計に注目しています。私は、ソリッドデザインやGOOSアイデアのような原則を使ったオブジェクト指向設計に続いて、モックをどのようにインスタンス化するかを選択することがずっと重要だと感じています。 – Brice

+0

@ブリスなぜですか?テスト中のクラス( 'ArticleManager')に依存性があまりにも大きければ、私はそれをはっきりと見ていきます。そして、これは主題から外れています。疑問はちょうど「モックをインスタンス化する方法について」ですか? – gontard

+0

私は「あまり明白ではない」と言わねばなりませんでした:)実際、@injectMocksは、ユーザーがワイヤーを使わなくても自動的にモックを注入しようと試みます(私たちは春やguiceでやっているように)コンストラクタインジェクションかセッターインジェクションかどうかはわかりませんが、このオブジェクトの将来の使用には煩わしいことがあります(再利用性は良いOO設計の主な利点の1つです)。 – Brice

9

の痛みが少なく、これを行うのきちんとした方法があります。

  • それはユニットテストだ場合は、これを行うことができます。それはあなたがこの(Springとその方法を使用するものではありませんちょうどそのショーケースを行うことができます統合テストだ場合:

    @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() { 
        } 
    } 
    
  • EDITをあなたは)diferentランナーとのモックを初期化することができます。

    @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

+1

実際それはあなたの権利ではありません。私はちょうどMockitoの可能性を見せたいと思っていました。たとえば、RESTFuseを使用する場合は、ランナーにユーザーを登録する必要があるため、MockitoAnnotationsでモックを初期化できます。initMocks(this); – emd

5

MockitoAnnotations &ランナーはよく上で議論されているので、私は愛されていないため、私のtuppenceに投げるつもりです:

XXX mockedXxx = mock(XXX.class); 

私はそれが少しより説明を見つけると私は(ない権利禁止から)好むので、私はこれを使用するユニット私のテストが自己完結型であることが好きなので、メンバ変数を使用しないようにテストします。

+0

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

+0

私の知る限りではありません。 –

+2

テストを読むために魔法を理解する必要が少なくなる。あなたは変数を宣言し、値を与えます。注釈や反映などはありません。 – Karu

17

mockをインスタンス化する第4の方法があります。これは、JUnit4 のルールMockitoRuleという名前を使用しています。

@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はsubclasses of TestRule annotated with @Ruleを探し、そしてランナーはを提供することをテスト文をラップにそれらを使用しています。これの結末は@Beforeメソッド、@Afterメソッドを抽出し、ラッパーをルールにキャッチしようとすることです。 ExpectedExceptionのように、テスト内からこれらとやりとりすることもできます。

MockitoRuleはあなたが(あなたのテストが複数回実行することができますので、あなたのテストのコンストラクタは引数を取ることができます)などParameterizedなどの他のランナーを、使用することができることを除いて、ほぼ正確にMockitoJUnitRunnerようを振る舞い、またはRobolectricのテストランナー(そうそのクラスローダーはAndroidネイティブクラスのJava置換を提供することができます)。これにより、最近のJUnitとMockitoのバージョンで厳密に柔軟に使用できます。要約すると

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

参照:How JUnit @Rule works?