Ответ 1
Для инициализации mocks использование runner или 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
) можно использовать, если вы уже настроили конкретный бегун (например, t23 > ) в тестовом случае.
Второе решение (с MockitoJUnitRunner
) является более классическим и моим любимым. Код проще. Использование бегуна предоставляет большое преимущество автоматическая проверка использования каркаса (описывается @Дэвид Уоллес в этот ответ).
Оба решения позволяют делиться макетами (и шпионами) между методами тестирования. В сочетании с @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
). Нет кода шаблона.
Минусы: тест не является самодостаточным, меньше боли кода