Hello, EasyMock!

easymock-logoВ статьях о JUnit я использовал в качестве примеров простые самодостаточные классы, которые не имели никаких зависимостей. На практике такие классы встречаются редко: обычно класс, выполняющий хоть какие-либо действия, зависит от других классов. Примером может служить классическая архитектура с многослойными приложениями, когда один слой обращается (=зависит) от другого. Как тестировать такие объекты?

Можно попробовать создать все необходимые объекты и протестировать класс с ними. В некоторых условиях это даже может сработать 🙂 Но, во первых, это противоречит основам юнит-тестирования — тестирование отдельной части программы, а не отдельной части и всех её зависимостей. Во вторых цепочка зависимостей может вести в базу данных, какой-нибудь внешний сервис итд. В третьих, поведение этих зависимостей может быть в принципе непредсказуемым: что вернёт Random?

В примере знакомства со Spring при написании тестов были написаны и отдельные реализации зависимостей, написанные специально для тестов (stubs).

Такие дублёры позволяют подменять реальные объекты, используемые тестируемым классом, на их симуляцию, имеющую предсказуемое поведение. Вместо использования реальных объектов, реализующих зависимости тестируемого класса, используются упрощённые объекты, сделанные специально для теста.

В том же примере «Hello, Spring!» я написал для каждого интерфейса две реализации — основную (например CoinImpl) и тестовую (MockCoin). И скажу вам прямо: мне было крайне неохота писать тестовые реализации. А если бы интерфейсов в программе было не 3, а хотя бы 12, пришлось написать бы 24 их реализации. И что самое неприятное, если бы мне потребовалось разное поведение этих реализаций в разных тестах, пришлось бы писать ещё больше! Жуть.

, наравне с другими фреймворками для создания тест-дублёров (а это, кстати, официальный термин, пропагандируемый аж самим Мартином Фаулером), автоматизирует и упрощает процесс создания тестовых реализаций интерфейсов.

Подготовка

Как обычно, начнём с пустого maven проекта:

И добавим к нему, помимо обычных Junit и Hamcrest, ещё и EasyMock:

В заключении поместим туда же классы Coin, GreeterTarget и GreeterTargetImpl  из примера Hello, Spring!

Самый простой

Код теста с использованием EasyMock настолько прост, что в нём даже нечего комментировать:

Разберём код теста подробно:

EasyMockRule это правило JUnit, которое и реализует всю магию — обрабатывает переменные с аннотациями @Mock и @TestedObject.

Каждая переменная с аннотацией @Mock обрабатывается правилом EasyMockRule и из неё создаётся mock объект. Состояние этого mock объекта будет сбрасываться к исходному автоматически, при вызове каждого теста.

@TestSubject указывает EasyMock, какая из переменных класса является тестируемым классом. EasyMock использует эту информацию для автоматического связывания mock объектов с тестируемым объектом. Подробно вопросы связывания я рассмотрю в отдельной статье.

Самый первый тест начинается с определения поведения mock объекта Coin: мы говорим, что будет вызван метод get() и что в ответ он должен вернуть true.

Посмотрите, насколько это проще, чем создание отдельной реализации интерфейса Coin, служащей только для тестов! Минимальная реализация такой заглушки требует как минимум дополнительной функции и хранит своё состояние:

Вызов replay(coinMock) говорит EasyMock, что мы закончили с определением поведения mock объектов и дальнейшие вызовы должны воспроизводить заданное поведение.

Второй тест ещё короче:

Здесь даже не определяется никакое поведение. Почему? Потому что в аннотации @Mock я указал, что хочу так называемый «nice mock». Nice mocks облегчают труд по созданию тестовых дублёров — все публичные методы nice mock возвращают значение по умолчанию, если их поведение не переопределено:

  • 0 для чисел
  • false для Boolean
  • null для строк и прочих объектов

поэтому во втором тесте мы используем значение по умолчанию и пропускаем задание поведения.

Код примера доступен на github.