Вопрос по junit, java, mockito – Инициализация ложных объектов - MockIto

93

Существует много способов инициализации фиктивного объекта с использованием MockIto. Какой из них лучший?

1.

 public class SampleBaseTestCase {

   @Before public void initMocks() {
       MockitoAnnotations.initMocks(this);
   }

2.

@RunWith(MockitoJUnitRunner.class)

[РЕДАКТИРОВАТЬ] 3.

mock(XXX.class);

подскажите, есть ли другие способы лучше этих ...

Ваш Ответ

5   ответов
113

Для ложной инициализациис помощью бегуна илиMockitoAnnotations.initMocks строго эквивалентные решения. От JavadocMockitoJUnitRunner:

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) является более классическим и моим любимым. Код проще. Использование бегуна дает большое преимуществоавтоматическая проверка использования фреймворка (описано@ Дэвид Уоллес вэтот ответ).

Оба решения позволяют разделять макеты (и шпионы) между методами испытаний. В сочетании с@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));
    }
}

Плюсы: код минимален

Минусы: черная магия. IMO это в основном из-за аннотации @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 создается) Нет стандартного кода.

Минусы: тест не является автономным, меньше боли кода

@ Дэвид Укажите правильную точку в вашем ответе. как насчет третьего? этот метод предлагается? VinayVeluri
@mlk Я полностью с тобой согласен. Мой английский не очень хорош и в нем нет нюансов. Моя цель состояла в том, чтобы настаивать на слове UNIT. gontard
Я должен был сказать "менее очевидно" :) Так что на самом деле @injectMocks попробуетЛучше всего автоматически вводить макеты, не требуя от пользователя каких-либо проводов (как мы привыкли делать весной или в хитрости), поэтому создание экземпляров объекта скрыто.не знаю, если этоВнедрение в конструктор или в сеттер, что может быть неприятно для будущего использования этого объекта (многократное использование - одно из главных преимуществ хорошего ОО-дизайна). Brice
@ Брайс, я согласен с твоим комментарием, VinayVeluri
8

поэтому яЯ собираюсь бросить в моем tuppence для нелюбимого

XXX mockedXxx = mock(XXX.class);

Я использую это, потому что нахожу это немного более описательным, и я предпочитаю (не без права запрета) модульные тесты не использовать переменные-члены, так как мне нравится, когда мои тесты (насколько они могут быть) самодостаточны.

Меньше волшебства, чтобы понять, чтобы прочитать тест. Вы объявляете переменную и даете ей значение - без аннотаций, рефлексии и т. Д. Karu
Есть ли какие-либо другие преимущества по сравнению с использованием mock (XX.class), за исключением того, что тестовый пример должен быть автономным? VinayVeluri
@ Кару Хороший вопрос. Michael Lloyd Lee mlk
Не настолько, насколько я знаю. Michael Lloyd Lee mlk
9

Если оно'С помощью модульного теста вы можете сделать это:

@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() {
    }
}

РЕДАКТИРОВАТЬ: Если этоС помощью интеграционного теста вы можете сделать это (не предназначено для такого использования в 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() {
    }
}
на самом деле это победилот, ваше право. Я просто хотел показать возможности Mockito. Например, если вы используете RESTFuse, вы должны использовать их бегун, чтобы вы могли инициализировать mock с помощью MockitoAnnotations.initMocks (this); emd
Если MOCK также участвует в интеграционных тестах, будет ли это иметь смысл? VinayVeluri
0

если вы хотите / нуждаетесь в них.

В дополнение к этому, я хотел бы добавить TL; DR:

Предпочитаю использовать@RunWith(MockitoJUnitRunner.class)Если вы не можете (потому что вы уже используете другого бегуна), предпочтите использовать@Rule public MockitoRule rule = MockitoJUnit.rule();Аналогично (2), но вы должныне используйте это больше:@Before public void initMocks() { MockitoAnnotations.initMocks(this); }Если вы хотите использовать макет только в одном из тестов и нене хотите выставлять его другим тестам того же класса, используйтеX x = mock(X.class)

(1) и (2) и (3) являются взаимоисключающими.

(4) может использоваться в сочетании с другими.

23

ляров mock, использующий 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() { /* ... */ }
}

Юнит ищетподклассы TestRule с аннотацией @Ruleи использует их дляоберните тестовые заявления, которые предоставляет бегун, В результате вы можете извлекать методы @Before, методы @After и даже пытаться ... ловить оболочки в правила. Вы можете даже взаимодействовать с ними из своего теста, такExpectedException делает.

MockitoRule ведет себяпочти так же, как MockitoJUnitRunner, за исключением того, что вы можете использовать любой другой бегун, такой какпараметризованных (что позволяет вашим конструкторам тестов принимать аргументы, чтобы ваши тесты могли выполняться несколько раз), или Robolectric 's тестовый прогон (так что его загрузчик классов может обеспечить замену Java для собственных классов Android). Это делает его более гибким для использования в последних версиях JUnit и Mockito.

В итоге:

Mockito.mock(): Прямой вызов без поддержки аннотаций или проверки использования.MockitoAnnotations.initMocks(this): Поддержка аннотаций, без проверки использования.MockitoJUnitRunner: Поддержка аннотаций и проверка использования, но вы должны использовать этот бегун.MockitoRule: Поддержка аннотаций и проверка использования с любым бегуном JUnit.

Смотрите также:Как работает JUnit @Rule?

В Котлине правило выглядит так:@get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule() Cristan

Похожие вопросы