Для инициализации 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 ). Нет шаблонного кода.
Минусы: тест не самодостаточен, меньше проблем с кодом.
MockitoJUnitRunner
. Для получения дополнительной информации о различиях см. Вопрос на stackoverflow.com/questions/10806345/… и мой ответ на него.Collaborator collab = mock(Collaborator.class)
, на мой взгляд, это, безусловно, правильный подход. Хотя это может быть многословным, вы можете получить понятность и возможность рефакторинга тестов. У обоих способов есть свои плюсы и минусы, я еще не решил, какой подход лучше. Эмиуэй всегда можно написать чушь, и это, вероятно, зависит от контекста и кодировщика.Теперь (начиная с версии 1.10.7) существует четвертый способ создания моков, который использует правило JUnit4 под названием MockitoRule .
JUnit ищет подклассы TestRule, аннотированные @Rule , и использует их для обертывания тестовых выражений , которые предоставляет Runner . В результате вы можете извлекать методы @Before, методы @After и даже пытаться ... перехватывать обертки в правила. Вы даже можете взаимодействовать с ними из своего теста, как это делает ExpectedException .
MockitoRule ведет себя почти так же, как MockitoJUnitRunner , за исключением того, что вы можете использовать любой другой бегун, например Parameterized (который позволяет вашим конструкторам тестов принимать аргументы, чтобы ваши тесты можно было запускать несколько раз), или средство запуска тестов Robolectric (так что его загрузчик классов может предоставлять замену Java. для собственных классов Android). Это делает его более гибким в использовании в последних версиях JUnit и Mockito.
В итоге:
Mockito.mock()
: Прямой вызов без поддержки аннотаций или проверки использования.MockitoAnnotations.initMocks(this)
: Поддержка аннотаций, без проверки использования.MockitoJUnitRunner
: Поддержка аннотаций и проверка использования, но вы должны использовать этот бегун.MockitoRule
: Поддержка аннотаций и проверка использования с любым исполнителем JUnit.См. Также: Как работает JUnit @Rule?
источник
@get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
Есть отличный способ сделать это.
Если это модульный тест, вы можете сделать это:
РЕДАКТИРОВАТЬ: если это тест интеграции, вы можете сделать это (не предназначено для использования таким образом с Spring. Просто продемонстрируйте, что вы можете инициализировать макеты с разными Runners):
источник
MockitoAnnotations и раннер были хорошо обсуждены выше, поэтому я собираюсь добавить свою помощь нелюбимым:
Я использую это, потому что считаю его немного более описательным, и я предпочитаю (а не из-за правильного запрета) модульные тесты не использовать переменные-члены, поскольку мне нравится, чтобы мои тесты были (насколько это возможно) самодостаточными.
источник
Небольшой пример для JUnit 5 Jupiter, «RunWith» был удален, теперь вам нужно использовать расширения, используя аннотацию «@ExtendWith».
источник
Другие ответы великолепны и содержат более подробную информацию, если они вам нужны / нужны.
В дополнение к этому я хотел бы добавить TL; DR:
@RunWith(MockitoJUnitRunner.class)
@Rule public MockitoRule rule = MockitoJUnit.rule();
@Before public void initMocks() { MockitoAnnotations.initMocks(this); }
X x = mock(X.class)
(1), (2) и (3) исключают друг друга.
(4) можно использовать в сочетании с другими.
источник