MockItoを使用してモックオブジェクトを初期化する方法は多数あります。これらの中で最善の方法は何ですか?
1。
public class SampleBaseTestCase {
@Before public void initMocks() {
MockitoAnnotations.initMocks(this);
}
2。
@RunWith(MockitoJUnitRunner.class)
[編集] 3。
mock(XXX.class);
これらよりも良い方法が他にあるなら私に提案してください...
モックの初期化では、ランナーまたは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
のインスタンス化方法)を明確に示します。定型コードはありません。
短所:テストは自己完結型ではなく、コードの痛みが少ない
(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の仕組み
これを行うきちんとした方法があります。
単体テストの場合、これを実行できます。
@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)は、他と組み合わせて使用できます。