Статьи

Mockito — плюсы, минусы и лучшие практики

Прошло почти 4 года с тех пор, как я написал пост в блоге под названием « EasyMock — плюсы, минусы и лучшие практики» , и с тех пор многое произошло. Вы больше ничего не слышали о EasyMock , и Mockito, похоже, заменил его в уме И по уважительной причине: это лучше.

Хороший гуманный интерфейс для
создания заглушек Как и EasyMock, Mockito позволяет объединять вызовы методов для создания менее императивного кода. Вот как можно создать заглушку для канонического объекта Warehouse:

Warehouse mock = Mockito.mock(Warehouse.class);
Mockito.when(mock.hasInventory(TALISKER, 50)).
        thenReturn(true);

Я знаю, мне нравится безумное форматирование. Независимо от этого, предоставление непрямого ввода вашей тестируемой системы (SUT) не может быть проще. По сравнению с EasyMock нет больших преимуществ в отношении работы с заглушками и передачи заглушки в SUT. Давать косвенный ввод с помощью макетов, а затем использовать стандартные утверждения JUnit впоследствии просто с обоими инструментами, и оба поддерживают стандартные средства сравнения Hamcrest.

Класс (не только интерфейс) Mocks
Mockito позволяет макетировать классы, а также интерфейсы. Я знаю, что EasyMock ClassExtensions позволил вам сделать это, но немного приятнее иметь все это в одном пакете с Mockito.

Поддержка Test Шпионов, а не только Mocks
Существует разница между шпионами и издевается, Заглушки позволяют вам давать непрямой ввод в тест (значения читаются, но никогда не записываются), шпионы позволяют собирать непрямой вывод из теста (макет записывается и проверяется, но не дает входных данных теста), и макеты оба (ваш объект дает косвенный вход в ваш тест через Stubbing и собирает косвенный вывод через шпионаж). Разница проиллюстрирована между двумя примерами кода. В EasyMock у вас есть только макеты. Вы должны установить все ожидания ввода и вывода перед запуском теста, а затем проверить позже. 

// arrange
Warehouse mock = EasyMock.createMock(Warehouse.class); 
EasyMock.expect(
    mock.hasInventory(TALISKER, 50)).
    andReturn(true).once();
EasyMock.expect(
    mock.remove(TALISKER, 50)).
    andReturn(true).once();
EasyMock.replay(mock); 

//act
Order order = new Order(TALISKER, 50);
order.fill(warehouse); 

// assert
EasyMock.verify(mock);

Это много кода, и не все это нужно. Раздел аранжировки настраивает заглушку (на складе есть инвентарь) и настраивает фиктивное ожидание (метод удаления будет вызван позже). Утверждение во всем этом на самом деле является маленьким методом verify () в конце. Суть этого теста в том, что вызывается метод remove (), но эта информация скрывается в гнезде ожиданий. В Mockito это улучшается за счет исключения как режима записи / воспроизведения, так и общего метода verify (). Короче и понятнее:

// arrange
Warehouse mock = Mockito.mock(Warehouse.class);
Mockito.when(mock.hasInventory(TALISKER, 50)).
        thenReturn(true);

//act
Order order = new Order(TALISKER, 50);
order.fill(warehouse); 

// assert
Mockito.verify(warehouse).remove(TALISKER, 50);

Шаг проверки с Mockito следит за результатами теста, а не записывает и проверяет. Меньше кода и более четкая картина того, что на самом деле ожидается. Обновление: в Mockito также можно использовать отдельный Spy API: http://mockito.googlecode.com/svn/branches/1.8.3/javadoc/org/mockito/Mockito.html#13

Лучшая обработка метода Void

Mockito обрабатывает пустые методы лучше, чем EasyMock. Свободный API отлично работает с методом void, но в EasyMock было несколько специальных методов, которые вам пришлось написать. Во-первых, код Mockito довольно прост для чтения:

// arrange
Warehouse mock = Mockito.mock(Warehouse.class);

//act
Order order = new Order(TALISKER, 50);
order.fill(warehouse); 

// assert
Mockito.verify(warehouse).remove(TALISKER, 50);

Здесь то же самое в EasyMock. Не так хорошо:

// arrange
Warehouse mock = EasyMock.createMock(Warehouse.class); 
mock.remove(TALISKER, 50); 
EasyMock.expectLastMethodCall().once(); 
EasyMock.replay(mock); 

//act
Order order = new Order(TALISKER, 50);
order.fill(warehouse); 

// assert
EasyMock.verify(mock); 

Шаблоны организации макетов объектов

И Mockito, и EasyMock страдают от сложного технического обслуживания. То, что я сказал в своем оригинальном посте EasyMock, справедливо для Mockito:

Интерфейс стилей цепочки методов легко написать, но мне трудно его прочитать. Когда тест, отличный от того, над которым я работаю, не удался, зачастую очень трудно определить, что именно происходит. В итоге мне приходится проверять производственный код и тестовый код ожидания для диагностики проблемы. Свернутые вручную фиктивные объекты намного легче диагностировать, когда что-то ломается … Эта проблема особенно неприятна после рефакторинга кода ожидания для уменьшения дублирования. На протяжении жизни я не могу следовать коду ожидания, который был преобразован в общие методы.

Теперь, четыре года спустя, у меня есть решение, которое хорошо работает для меня. С небольшой осторожностью вы можете сделать ваши макеты многоразовыми, ремонтопригодными и читаемыми. Этот подход был проверен в течение многих месяцев в корпоративной среде (тм). Создайте приватный статический метод в первый раз, когда вам понадобится макет. Любые важные данные должны быть переданы в качестве параметра. Использование констант или «магических» полей скрывает важную информацию и запутывает тесты. Например:

User user = createMockUser("userID", "name"); 
...
assertEquals("userID", result.id()); 
assertEquals("name", result.name(); 

Все важное видно и в тесте ничего важного не спрятано. Вам нужно полностью скрыть состояние воспроизведения за этим фабричным методом, если вы все еще используете EasyMock. Используемая среда Mock является деталью реализации и старается не допустить ее утечки.

Затем, по мере роста ваших зависимостей, обязательно всегда передавайте их как параметры метода фабрики. Если вам нужен объект User и Role, не создавайте один метод, который создает оба макета. Один метод создает экземпляр одного объекта, в противном случае он является параметром и создает макеты объектов в методе test:

User user = createMockUser(
    "userID", 
    "name", 
    createMockRole("role1"), 
    createMockRole("role2")
); 

Когда у каждого типа объекта есть фабричный метод, это значительно упрощает объединение различных типов объектов. Повторное использование. Но вы можете использовать методы только тогда, когда они просты и имеют мало зависимостей, в противном случае они становятся слишком конкретными и трудными для понимания. В первый раз вам необходимо повторно использовать один из этих методов, а затем переместить метод в служебный класс с именем «* Mocker», например UserMocker или RoleMocker. Следуйте соглашению об именах, чтобы их всегда было легко найти. Если вы не забыли сделать статичные методы фабрики статичными, перемещение их должно быть очень простым. Ваш клиентский код в конечном итоге выглядит следующим образом, но вы можете использовать статический импорт, чтобы исправить это:

User user = UserMocker.createMockUser(
    "userID", 
    "name", 
    RoleMocker.createMockRole("role1"), 
    RoleMocker.createMockRole("role2")
); 

Пользователь перегружен методами свободно. Не создавайте один гигантский метод с каждым возможным параметром в списке параметров. Есть веские причины избегать перегрузок в производстве, но это тест. Используйте перегрузку, чтобы методы тестирования отображали только данные, относящиеся к этому тесту, и ничего более. Использование Varargs также может помочь в проведении чистого теста. Наконец, не используйте константы. Константы скрывают важную информацию вне поля зрения, в верхней части файла, где вы ее не видите, или в классе Mocker. Можно использовать константы в тестовом примере, но не определяйте константы в макетах, это просто скрывает соответствующую информацию и затрудняет чтение теста позже.

Избегайте абстрактных тестовых случаев

Управление фиктивными объектами в абстрактных тестовых случаях было для меня очень трудным, особенно при управлении состояниями воспроизведения и записи. Я прекратил смешивать фиктивные объекты и абстрактные объекты TestCase. Когда что-то ломается, диагностика занимает слишком много времени. Альтернативой является создание пользовательских методов утверждения, которые можно использовать повторно. Кроме того, я все равно отказался от объектов Abstract TestCase из-за предпочтения композиции наследования.

Не заменяйте утверждения проверкой

Мои оригинальные комментарии о EasyMock по-прежнему актуальны для Mockito: самые простые методы для понимания и тестирования — это методы, которые выполняют какую-то работу. Вы запускаете метод, а затем используете утверждения, чтобы убедиться, что все работает. Напротив, фиктивные объекты облегчают тестирование делегирования, когда некоторый объект, отличный от SUT, выполняет работу. Делегирование означает, что целью метода является создание побочного эффекта, а не выполнение работы. Иногда требуется побочный код, но зачастую его сложнее понять и отладить. На самом деле, некоторые языки этого даже не позволяют! Если ваш тестовый код содержит методы assert, у вас есть хороший тест. Если ваш код не содержит утверждений, а содержит длинный список вызовов verify (), то вы полагаетесь на побочные эффекты. Это плохой запах юнит-теста,особенно если есть несколько объектов, которые необходимо проверить. Проверка нескольких объектов в конце модульного теста — это как сказать: «Мой метод тестирования должен выполнять несколько вещей: x, y и z». Устав и ответственность метода больше не ясны. Это кандидат на рефакторинг.

Нет больше всего или ничего тестирования

Методы verify () в Mockito гораздо более гибкие, чем в EasyMock. Вы можете проверить, что были вызваны только один или два метода на макете, в то время как у EasyMock был только один грубый метод verify (). С EasyMock я закончил засорять код бессмысленными ожиданиями, но в Mockito это было не так. Одной этой причины достаточно для переключения.

Отказ: ожидаемый X получил X

По большей части сообщения об ошибках Mockito лучше, чем EasyMock. Тем не менее, вы все еще иногда видите сбой, который гласит «Сбой. Получил Х Ожидаемый Х». По сути, это означает, что ваши методы toString () дают те же результаты, но equals () — нет. Каждый начинающий пользователь в какой-то момент запутывается в этом сообщении. Имейте в виду.

Не прекращайте ручные манипуляции

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

Научитесь писать ArgumentMatcher

Научитесь писать ArgumentMatcher . Кривая обучения есть, но она быстро заканчивается. Этот пост достаточно длинный, поэтому я не буду приводить пример.

Вот и все. Увидимся через 4 года, когда выйдет следующая структура!

 От http://hamletdarcy.blogspot.com/2010/09/mockito-pros-cons-and-best-practices.html