Подготовка макетов объектов - MockIto
есть много способов инициализировать макет объекта с помощью MockIto.
Что является лучшим способом среди них ?
1.
public class SampleBaseTestCase {
@Before public void initMocks() {
MockitoAnnotations.initMocks(this);
}
2.
@RunWith(MockitoJUnitRunner.class)
[редактирование]
3.
mock(XXX.class);
предложите мне, если есть какие-либо другие способы лучше, чем эти...
4 ответов:
для инициализации глумится, используя бегунок или
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инстанцируется). Нет шаблона код.минусы: тест не является самодостаточным, меньше боли кода
теперь есть (по состоянию на v1.10.7) четвертый способ создания экземпляров mocks, который использует JUnit4 правила под названием MockitoRule.
@RunWith(JUnit4.class) // or a different runner of your choice public class YourTest @Rule public MockitoRule rule = MockitoJUnit.rule(); @Mock public YourMock yourMock; @Test public void yourTestMethod() { /* ... */ } }JUnit ищет подклассы TestRule аннотируется @правилу, и использует их в оберните тестовые операторы, которые предоставляет Бегун. В результате этого вы можете извлечь @Before методы, @After методы и даже попробовать...ловите обертки в правила. Вы даже можете взаимодействовать с ними из вашего теста, таким образом, что ExpectedException делает.
MockitoRule ведет себя почти так же, как MockitoJUnitRunner, за исключением того, что вы можете использовать любой другой участник, например параметризованные (что позволяет вашим конструкторам тестов принимать аргументы, чтобы ваши тесты можно было запускать несколько раз), или Robolectric Test runner (поэтому его загрузчик классов может предоставлять замены Java для собственных классов Android). Это делает его строго более гибким к используйте в последних версиях JUnit и Mockito.
в итоге:
Mockito.mock(): прямой вызов без поддержки аннотаций или проверки использования.MockitoAnnotations.initMocks(this): поддержка аннотаций, без использования проверки.MockitoJUnitRunner: поддержка аннотаций и проверка использования, но вы должны использовать это бегун.MockitoRule: поддержка аннотаций и проверка использования с любым JUnit runner.Читайте также: Как JUnit @Правило работает?
есть аккуратный способ сделать это.
Если это модульный тест, вы можете сделать это:
@RunWith(MockitoJUnitRunner.class) public class MyUnitTest { @Mock private MyFirstMock myFirstMock; @Mock private MySecondMock mySecondMock; @Spy private MySpiedClass mySpiedClass = new MySpiedClass(); // It's gonna inject the 2 mocks and the spied object per reflection to this object // The java doc of @InjectMocks explains it really well how and when it does the injection @InjectMocks private MyClassToTest myClassToTest; @Test public void testSomething() { } }EDIT: если это интеграционный тест, вы можете сделать это(не предназначено для использования таким образом с Spring. Просто продемонстрируйте, что вы можете инициализировать насмешки с разными бегунами):
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("aplicationContext.xml") public class MyIntegrationTest { @Mock private MyFirstMock myFirstMock; @Mock private MySecondMock mySecondMock; @Spy private MySpiedClass mySpiedClass = new MySpiedClass(); // It's gonna inject the 2 mocks and the spied object per reflection to this object // The java doc of @InjectMocks explains it really well how and when it does the injection @InjectMocks private MyClassToTest myClassToTest; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); } @Test public void testSomething() { } }
MockitoAnnotations & The runner были хорошо обсуждены выше, поэтому я собираюсь бросить свой tuppence для нелюбимого:
XXX mockedXxx = mock(XXX.class);Я использую это, потому что я нахожу его немного более описательным, и я предпочитаю (не из правильного запрета) модульные тесты не использовать переменные-члены, поскольку мне нравится, чтобы мои тесты были (насколько они могут быть) самодостаточными.
Comments