Для инициализации mocks использование runner или the 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));
}
}
Плюсы: Код минимальный
Минусы: Черная магия. ИМО, это в основном из-за аннотации @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создается экземпляр my ). Нет шаблонного кода.
Минусы: тест не самодостаточен, меньше проблем с кодом.