Для инициализации 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 ). Нет шаблонного кода.
Минусы: тест не самодостаточен, меньше проблем с кодом.