Инициализация ложных объектов - MockIto

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

1.

 public class SampleBaseTestCase {

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

2.

@RunWith(MockitoJUnitRunner.class)

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

mock(XXX.class);

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

Ответы на вопрос(5)

Решение Вопроса

Для ложной инициализациис помощью бегуна или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 создается) Нет стандартного кода.

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

 VinayVeluri20 мар. 2013 г., 07:45
@ Дэвид Укажите правильную точку в вашем ответе. как насчет третьего? этот метод предлагается?
 gontard20 мар. 2013 г., 10:50
@mlk Я полностью с тобой согласен. Мой английский не очень хорош и в нем нет нюансов. Моя цель состояла в том, чтобы настаивать на слове UNIT.
 Brice19 мар. 2013 г., 11:50
Я должен был сказать "менее очевидно" :) Так что на самом деле @injectMocks попробуетЛучше всего автоматически вводить макеты, не требуя от пользователя каких-либо проводов (как мы привыкли делать весной или в хитрости), поэтому создание экземпляров объекта скрыто.не знаю, если этоВнедрение в конструктор или в сеттер, что может быть неприятно для будущего использования этого объекта (многократное использование - одно из главных преимуществ хорошего ОО-дизайна).
 VinayVeluri19 мар. 2013 г., 13:47
@ Брайс, я согласен с твоим комментарием,
 Dawood ibn Kareem19 мар. 2013 г., 14:02
НЕ ПРАВИЛЬНО, что эти два эквивалентны. Неверно, что простой код является единственным преимуществом использованияMockitoJUnitRunner, Для получения дополнительной информации о различиях см. Вопрос наstackoverflow.com/questions/10806345/... и мой ответ на него.
 Brice19 мар. 2013 г., 16:09
@gontard Даже использование инъекции в конструктор может стать неправильным.@InjectMocks ISN»Придирчив и попытайтесь выбрать лучшее соответствие, так что это зависит от количества зависимостей и кода конструкторов в любом случае, но, опять же, кодировщик может не чувствовать опыт создания и связывания этих соавторов. И может упустить возможность использовать другие подходы, такие как подход строителя (Joshua Bloch) или провайдеры (такие же, как guava)поставщики).
 Brice19 мар. 2013 г., 11:51
(продолжение) Если вы нене вижу, как этот объект создан, вы неЯ не чувствую боли по этому поводу, и будущие программисты могут не отреагировать хорошо, если будут добавлены новые функции. В любом случае это спорно оба пути, яЯ просто говорю быть осторожным с этим.
 VinayVeluri19 мар. 2013 г., 13:49
@ Гонтард, я понял последнее более ясно, суть в том, чтобы сделать тестовый пример более понятным, это то, что это значит?
 gontard19 мар. 2013 г., 16:14
ИМХО количество зависимостей хорошо видно в тесте. И цель модульного теста - проверить класс, а не его создатель.
 Brice19 мар. 2013 г., 11:06
Будьте осторожны, аннотации полезны, но они неt защитить вас от создания плохого ОО дизайна (или его ухудшения). Лично я покаЯ счастлив уменьшить стандартный код, я теряю боль кода (или PITA), который является триггером для изменения дизайна на лучший, поэтому я и команда уделяем внимание дизайну ОО. Я чувствую, что следование ОО-дизайну с такими принципами, как ТВЕРДЫЙ дизайн или идеи ГСНО, гораздо важнее, чем выбор способа создания экземпляров макетов.
 gontard19 мар. 2013 г., 11:07
@ Брайс, почему? если тестируемый класс (ArticleManager) слишком много зависимостей, я это ясно увижу. И это не по теме, вопрос только о "как создавать экземпляры издевательств?
 Brice19 мар. 2013 г., 16:15
@Gontard Да, конечно, зависимости видны, но яЯ видел, что код не работает, используя этот подход. Об использованииCollaborator collab = mock(Collaborator.class)На мой взгляд, этот способ, безусловно, является правильным подходом. Хотя это может показаться многословным, вы можете получить понятность и рефакторируемость тестов. У обоих способов есть свои плюсы и минусы, яМы еще не решили, какой подход лучше. Эмивей этоВсегда можно написать дерьмо, и, вероятно, зависит от контекста и кодировщика.
 gontard19 мар. 2013 г., 14:54
@DavidWallace я исправляю свой ответ. Спасибо, что заметили это. И я тоже кое-что узнаю :) Не стесняйтесь редактировать и улучшать мой ответ.
 gontard19 мар. 2013 г., 11:59
Я согласен с тобой. Мне нравится идея PITA. И спасибо, я неНе знаю ГСНО, это кажется очень интересным. Персонал в моей компании, мы используем только инъекцию конструктора (без публичного сеттера | неизменяемого объекта), поэтому, когда я используюInjectMocksЯ действительно понимаю, что он делает. Хотя я предпочитаю способ, описанный @mlk в своем ответе (автономные тесты).
 Michael Lloyd Lee mlk20 мар. 2013 г., 10:43
@gontard В отношении того, для чего предназначены модульные тесты: я не согласен, смысл теста (в TDD) - помочь в разработке класса. Это также дает быструю обратную связь о том, работает ли класс как ожидалось, и является очень хорошей причиной для написания тестов, но не единственной причиной для их выполнения.

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

XXX mockedXxx = mock(XXX.class);

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

 Karu02 июн. 2015 г., 06:14
Меньше волшебства, чтобы понять, чтобы прочитать тест. Вы объявляете переменную и даете ей значение - без аннотаций, рефлексии и т. Д.
 VinayVeluri19 мар. 2013 г., 13:43
Есть ли какие-либо другие преимущества по сравнению с использованием mock (XX.class), за исключением того, что тестовый пример должен быть автономным?
 Michael Lloyd Lee mlk02 июн. 2015 г., 11:20
@ Кару Хороший вопрос.
 Michael Lloyd Lee mlk19 мар. 2013 г., 15:12
Не настолько, насколько я знаю.

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

@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() {
    }
}
 emd19 мар. 2013 г., 13:48
на самом деле это победилот, ваше право. Я просто хотел показать возможности Mockito. Например, если вы используете RESTFuse, вы должны использовать их бегун, чтобы вы могли инициализировать mock с помощью MockitoAnnotations.initMocks (this);
 VinayVeluri19 мар. 2013 г., 13:42
Если MOCK также участвует в интеграционных тестах, будет ли это иметь смысл?

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

В дополнение к этому, я хотел бы добавить 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) может использоваться в сочетании с другими.

ляров 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?

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

Ваш ответ на вопрос