Для ініціалізації макетів використовуються бігун або MockitoAnnotations.initMocksстрого рівнозначні рішення. Від javadoc MockitoJUnitRunner :
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) - це більш класичне та моє улюблене. Код простіший. Використання бігуна забезпечує велику перевагу автоматичної перевірки використання фреймворку (описаний у цій відповіді @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 )
Третє рішення - створити свій макет над кожним методом тестування. Це дозволяє, як пояснив @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інстанцію). Відсутній код котла.
Мінуси: Тест не є автономним, менше больового коду