回答:
モックの初期化では、ランナーまたはの使用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
インスタンス化の方法)。定型コードはありません。
短所:テストは自己完結型ではないため、コードの苦痛が少ない
MockitoJUnitRunner
。違いの詳細については、stackoverflow.com / questions / 10806345 / …の質問とその回答を参照してください。
Collaborator collab = mock(Collaborator.class)
、私の意見では、この方法は確かに有効なアプローチです。これは冗長になりがちですが、テストの理解とリファクタリングを得ることができます。どちらの方法にも長所と短所があり、どちらの方法が優れているかはまだわかりません。Amywayは常にがらくたを書くことが可能であり、おそらくコンテキストとコーダーに依存します。
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ランナーでの注釈サポートと使用法検証。@get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
これを行うには、きちんとした方法があります。
単体テストの場合、これを行うことができます:
@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() {
}
}
MockitoAnnotationsとランナーについては上記で十分に説明しているので、愛していない人のためにタッペンを投入します。
XXX mockedXxx = mock(XXX.class);
私がこれを使用するのは、それがもう少し説明的であると思うので、私は自分のテストを(できるだけ)自己完結させたいので、メンバー変数を使用しない(適切な禁止ではない)単体テストを好むからです。
他の回答はすばらしいです。必要に応じて、詳細を記載してください。
これらに加えて、TL; DRを追加したいと思います。
@RunWith(MockitoJUnitRunner.class)
@Rule public MockitoRule rule = MockitoJUnit.rule();
@Before public void initMocks() {
MockitoAnnotations.initMocks(this);
}
X x = mock(X.class)
(1)と(2)と(3)は相互に排他的です。
(4)他と組み合わせて使用できます。