對於模擬初始化,使用亞軍或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
例如)可以使用。
第二個解決方案(與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是一種妥協。使用@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
實例化)。沒有樣板代碼。
缺點:測試不是自包含的代碼
不過要小心,註釋是有用的,但他們不保護你免受到工藝OO設計不佳(或貶低它)。就個人而言,儘管我很樂意減少樣板代碼,但我放鬆了代碼(或PITA)的痛苦,這是將設計更改爲更好的代碼的原因,因此我和團隊正在關注OO設計。我覺得使用SOLID設計或GOOS思想等原理來進行OO設計是非常重要的,因爲它可以選擇如何實例化mock。 – Brice 2013-03-19 10:06:55
@爲什麼?如果被測試的類('ArticleManager')具有太多依賴性,我會清楚地看到它。這是關於主題,問題只是「如何實例化模擬?」 – gontard 2013-03-19 10:07:07
我應該說「不那麼明顯」:)所以實際上@injectMocks會嘗試最好自動注入模擬而不需要用戶連線(比如我們在春季或者guice中使用),所以對象實例化是隱藏的,你不知道它是構造器注入還是setter注入,這可能會對這個對象的未來使用造成麻煩(可重用性是良好OO設計的主要優點之一)。 – Brice 2013-03-19 10:50:39