モックの初期化の場合、ランナーまたは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
がインスタンス化された方法)方法を示します。ボイラープレートコードはありません。
短所:テストは自己完結型ではありません、コード
は注意してくださいただし、注釈は便利ですが、彼らは貧しいOOデザインを作るために(またはそれを分解するために)に対してあなたを保護することはできません。個人的にはボイラープレートのコードを減らすことができて嬉しいですが、デザインをより良いものに変えるトリガーとなるコード(またはPITA)の痛みが緩んでいるので、私とチームはオブジェクト指向設計に注目しています。私は、ソリッドデザインやGOOSアイデアのような原則を使ったオブジェクト指向設計に続いて、モックをどのようにインスタンス化するかを選択することがずっと重要だと感じています。 – Brice
@ブリスなぜですか?テスト中のクラス( 'ArticleManager')に依存性があまりにも大きければ、私はそれをはっきりと見ていきます。そして、これは主題から外れています。疑問はちょうど「モックをインスタンス化する方法について」ですか? – gontard
私は「あまり明白ではない」と言わねばなりませんでした:)実際、@injectMocksは、ユーザーがワイヤーを使わなくても自動的にモックを注入しようと試みます(私たちは春やguiceでやっているように)コンストラクタインジェクションかセッターインジェクションかどうかはわかりませんが、このオブジェクトの将来の使用には煩わしいことがあります(再利用性は良いOO設計の主な利点の1つです)。 – Brice