Подтвердить что ты не робот

EasyMock против Mockito: дизайн и ремонтопригодность?

Один из способов думать об этом: если мы заботимся о дизайне кода, то EasyMock - лучший выбор, поскольку он дает вам обратную связь с его концепцией ожиданий.

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

Мои вопросы:

  • Если вы использовали EasyMock в крупномасштабных проектах, обнаружите, что ваши тесты сложнее поддерживать?
  • Каковы ограничения Mockito (кроме тестирования endo)?
4b9b3361

Ответ 1

Я разработчик EasyMock, немного неполный, но, конечно же, я использовал EasyMock для крупномасштабных проектов.

Мое мнение таково, что тесты EasyMock действительно будут ломаться время от времени. EasyMock заставляет вас делать полную запись того, что вы ожидаете. Это требует определенной дисциплины. Вы должны действительно записать то, что ожидается не то, что в настоящее время требуется испытанному методу. Например, если неважно, сколько раз метод вызывается на макет, не бойтесь использовать andStubReturn. Кроме того, если вам не нужен параметр, используйте anyObject() и так далее. Мышление в TDD может помочь в этом.

Мой анализ заключается в том, что тесты EasyMock будут ломаться чаще, но Mockito не будет, когда вы захотите их. Я предпочитаю, чтобы мои тесты ломались. По крайней мере, я знаю, каковы были последствия моего развития. Это, конечно, моя личная точка зрения.

Ответ 2

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

Учитывая: У нас есть класс, который отвечает за хранение чего-то где-то:

public class Service {

    public static final String PATH = "path";
    public static final String NAME = "name";
    public static final String CONTENT = "content";
    private FileDao dao;

    public void doSomething() {
        dao.store(PATH, NAME, IOUtils.toInputStream(CONTENT));
    }

    public void setDao(FileDao dao) {
        this.dao = dao;
    }
}

и мы хотим проверить его:

Mockito:

public class ServiceMockitoTest {

    private Service service;

    @Mock
    private FileDao dao;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        service = new Service();
        service.setDao(dao);
    }

    @Test
    public void testDoSomething() throws Exception {
        // given
        // when
        service.doSomething();
        // then
        ArgumentCaptor<InputStream> captor = ArgumentCaptor.forClass(InputStream.class);
        Mockito.verify(dao, times(1)).store(eq(Service.PATH), eq(Service.NAME), captor.capture());
        assertThat(Service.CONTENT, is(IOUtils.toString(captor.getValue())));
    }
}

EasyMock:

public class ServiceEasyMockTest {
    private Service service;
    private FileDao dao;

    @Before
    public void setUp() {
        dao = EasyMock.createNiceMock(FileDao.class);
        service = new Service();
        service.setDao(dao);
    }

    @Test
    public void testDoSomething() throws Exception {
        // given
        Capture<InputStream> captured = new Capture<InputStream>();
        dao.store(eq(Service.PATH), eq(Service.NAME), capture(captured));
        replay(dao);
        // when
        service.doSomething();
        // then
        assertThat(Service.CONTENT, is(IOUtils.toString(captured.getValue())));
        verify(dao);
    }
}

Как вы можете видеть, оба теста довольно одинаковы, и оба они проходят. Теперь давайте представим, что кто-то другой изменил реализацию службы и попытался запустить тесты.

Новая услуга:

dao.store(PATH + separator, NAME, IOUtils.toInputStream(CONTENT));

разделитель был добавлен в конце постоянной PATH

Как результаты тестов будут выглядеть прямо сейчас? Прежде всего, оба теста потерпят неудачу, но с различными сообщениями об ошибках:

EasyMock:

java.lang.AssertionError: Nothing captured yet
    at org.easymock.Capture.getValue(Capture.java:78)
    at ServiceEasyMockTest.testDoSomething(ServiceEasyMockTest.java:36)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)

Mockito:

Argument(s) are different! Wanted:
dao.store(
    "path",
    "name",
    <Capturing argument>
);
-> at ServiceMockitoTest.testDoSomething(ServiceMockitoTest.java:34)
Actual invocation has different arguments:
dao.store(
    "path\",
    "name",
    [email protected]
);
-> at Service.doSomething(Service.java:13)

Что произошло в тесте EasyMock, почему результат не был захвачен? Метод магазина не был выполнен, но подождать минуту, это было, почему EasyMock лежит на нас?

Это потому, что EasyMock смешивает две обязанности в одной строке - ступени и проверки. Поэтому, когда что-то не так, трудно понять, какая часть вызывает провал.

Конечно, вы можете сказать мне - просто измените тест и переместите проверку перед утверждением. Вау, ты серьезно, разработчики должны помнить о каком-то волшебном порядке, освещенном насмешливыми фреймами?

Кстати, это не поможет:

java.lang.AssertionError: 
  Expectation failure on verify:
    store("path", "name", capture(Nothing captured yet)): expected: 1, actual: 0
    at org.easymock.internal.MocksControl.verify(MocksControl.java:111)
    at org.easymock.classextension.EasyMock.verify(EasyMock.java:211)

Тем не менее, он говорит мне, что метод не был выполнен, но он был, только с другими параметрами.

Почему Mockito лучше? Эта структура не смешивает две обязанности в одном месте, и когда ваши тесты потерпят неудачу, вы легко поймете, почему.

Ответ 3

если мы заботимся о дизайне кода, тогда Easymock - лучший выбор, поскольку он дает вам обратную связь с его концепцией ожиданий

Интересно. Я обнаружил, что "концепция ожиданий" заставляет многих разработчиков вкладывать все больше и больше ожиданий в тесты только для удовлетворения проблемы UnexpectedMethodCall. Как это влияет на дизайн?

Тест не должен прерываться при изменении кода. Тест прекращается, когда функция перестает работать. Если вам нравится, когда тесты ломаются, когда происходит какое-либо изменение кода, я предлагаю написать тест, который утверждает контрольную сумму md5 java файла:)

Ответ 4

Я не думаю, что вы должны быть слишком обеспокоены этим. Оба Easymock и Mockito могут быть настроены на "строгую" или "приятную", единственная разница заключается в том, что по умолчанию Easymock строгий, но Mockito хорош.

Как и при всем тестировании, нет жесткого и быстрого правила, вам нужно сбалансировать уверенность теста в отношении ремонтопригодности. Я обычно нахожу, что есть определенные функциональные или технические области, которые требуют высокого уровня уверенности, для которого я бы использовал "строгие" издевательства. Например, мы, вероятно, не хотели бы, чтобы метод debitAccount() вызывался более одного раза! Однако есть и другие случаи, когда макет действительно немного больше, чем заглушка, поэтому мы можем проверить реальное "мясо" кода.

В первые дни работы API совместимости Mockito была проблема, но теперь больше инструментов поддерживают фреймворк. У Powermock (личный фаворит) теперь есть расширение mockito

Ответ 5

Я предпочитаю mockito, если честно. использует EasyMock с унилидами, и комбинация часто приводит к исключениям, таким как IllegalArgumentException: не интерфейс, а также MissingBehaviorExceptions. В обоих случаях код и тестовый код в порядке. Оказалось, что исключение MissingBehaviorException было связано с тем, что издевавшиеся объекты, созданные с помощью createMock (используя classextentions!!), действительно вызвали эту ошибку. При использовании @Mock это действительно сработало! Мне не нравится такое вводящее в заблуждение поведение, и для меня это явное указание, что разработчики этого не знают, что делают. Хорошая структура всегда должна быть простой в использовании, а не двусмысленной. Исключение IllegalArgumentException также было связано с некоторыми смешениями внутренних компонентов EasyMock. Кроме того, запись не является тем, что я хочу сделать. Я хочу проверить, не генерирует ли мой код исключения или нет, и что он возвращает ожидаемые результаты. Это в сочетании с охватом кода - правильный инструмент для меня. Я не хочу, чтобы мои тесты прерывались, когда я помещал 1 строку кода выше или ниже предыдущей, потому что это повышает производительность или так. С mockito это не проблема. С EasyMock это приведет к провалу тестов, даже если код не сломался. Это плохо. Это стоит времени, а значит и денег. Вы хотите проверить ожидаемое поведение. Вы действительно заботитесь о порядке вещей? Полагаю, в редких случаях вы можете. Затем используйте Easymock. В другом случае, я думаю, вы потратите значительно меньше времени, используя mockito, чтобы написать свои тесты.

С уважением Лоуренс