Для ініціалізації макетів використовуються бігун або 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
інстанцію). Відсутній код котла.
Мінуси: Тест не є автономним, менше больового коду