Что такое stubbing java

Моки, стабы и шпионы в Spock Framework

Spock предоставляет 3 мощных (но разных по сути) инструмента, упрощающих написание тестов: Mock, Stub и Spy.

Что такое stubbing java. Смотреть фото Что такое stubbing java. Смотреть картинку Что такое stubbing java. Картинка про Что такое stubbing java. Фото Что такое stubbing java

Довольно часто коду, который нужно протестировать, требуется взаимодействовать с внешними модулями, называющимися зависимостями (в оригинальной статье используется термин collaborators, который не очень распространён в русскоязычной среде).

Модульные тесты чаще всего разрабатываются для тестирования одного изолированного класса при помощи различных вариантов моков: Mock, Stub и Spy. Так тесты будут надёжнее и будут реже ломаться по мере того, как код зависимостей эволюционирует.

Такие изолированные тесты менее подвержены проблемам при изменении внутренних деталей реализации зависимостей.

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

Mocks

Используйте Mock для:

Stubs

Используйте Stub для:

Spies

Бойтесь шпионов (Spy). Как сказано в документации Spock:

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

Но так уж случается, что бывают ситуации, когда мы должны работать с легаси кодом. Легаси код бывает сложно или даже невозможно протестировать при помощи моков и стабов. В этом случае есть всего один вариант решения — использовать Spy.

Лучше иметь легаси код, покрытый тестами с использованием Spy, чем не иметь тестов для легаси совсем.

Используйте Spy для:

Mocks

Что такое stubbing java. Смотреть фото Что такое stubbing java. Смотреть картинку Что такое stubbing java. Картинка про Что такое stubbing java. Фото Что такое stubbing java

В этом сценарии мы хотим написать тест, который проверит:

Приведённый тест создаёт мок сервиса:

Приведённый код решает 4 важные задачи:

Stubs

Использует ли тестируемый код зависимости? Является ли целью тестирования удостовериться, что тестируемый код работает корректно при взаимодействии с зависимостями? Являются ли результаты вызовов методов зависимостей входными значениями для тестируемого кода?

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

Давайте посмотрим на следующий пример с FooController и FooService и протестируем функциональность контроллера при помощи стабов.

Создать стаб можно так:

Приведённый код решает 4 важные задачи:

Spies

Пожалуйста не читайте этот раздел.

Пропускайте и переходите к следующему.

Всё ещё читаете? Ну что ж, хорошо, давайте разбираться со Spy.

Что такое stubbing java. Смотреть фото Что такое stubbing java. Смотреть картинку Что такое stubbing java. Картинка про Что такое stubbing java. Фото Что такое stubbing java

Не используйте Spy. Как сказано в документации Spock:

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

При этом бывают ситуации, когда нам приходится работать с легаси кодом. Легаси код бывает невозможно протестировать с помощью моков или стабов. В этом случае шпион остаётся единственным жизнеспособным вариантом.

Шпионы отличаются от моков или стабов, потому что они работают не как заглушки.

Когда зависимость подменяется моком или стабом, создается тестовый объект, а настоящий исходный код зависимости не выполняется.

Шпион, с другой стороны, выполнит основной исходный код зависимости, для которой был создан шпион, но шпион позволит вам изменять то, что возвращает шпион, и проверять вызовы методов, так же как моки и стабы. (Отсюда и название Spy).

Создать экземпляр-шпион довольно просто:

Приведённый код решает 4 важные задачи:

Какой из вариантов использовать: Mock, Stub или Spy?

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

Q: Является ли целью тестирования проверка контракта между тестируемым кодом и зависимостями?

A: Если вы ответили Да, используйте Mock

Q: Является ли целью тестирования удостовериться, что тестируемый код работает верно при взаимодействии с зависимостями?

A: Если вы ответили Да, используйте Stub

Q: Являются ли результаты вызовов методов зависимостей входными значениями для тестируемого кода?

A: Если вы ответили Да, используйте Stub

Q: Работаете ли вы с легаси кодом, который очень сложно протестировать, и у вас не осталось вариантов?

A: Попробуйте использовать Spy

Код примеров

Вы можете найти код всех примеров этой статьи по ссылке:

Источник

vertigra / mock-object-conspect.md

Введение в mock-объекты. Классификация.

Часто тестируемый метод может вызывать методы других классов, которые в данном случае тестировать не нужно. Unit-тест потому и называется модульным, что тестирует отдельные модули, а не их взаимодействие. Причем, чем меньше тестируемый модуль – тем лучше с точки зрения будущей поддержки тестов. Для тестирования взаимодействия используются интеграционные тесты, где вы уже тестируете скорее полные use cases, а не отдельную функциональность.

Однако наши классы очень часто используют другие классы в своей работе. Например, слой бизнес логики (Business Logic layer) часто работает с другими объектами бизнес логики или обращается к слою доступа к данным (Data Access layer). В трехслойной архитектуре веб-приложений это вообще постоянный процесс: Presentation layer обращается к Business Logic layer, тот, в свою очередь, к Data Access layer, а Data Access layer – к базе данных. Как же тестировать подобный код, если вызов одного метода влечет за собой цепочку вплоть до базы данных?

В таких случаях на помощь приходят так называемые mock-объекты, предназначенные для симуляции поведения реальных объектов во время тестирования. Вообще, понятие mock-объект достаточно широко: оно может, с одной стороны, обозначать любые тест-дублеры (Test Doubles) или конкретный вид этих дублеров – mock-объекты.

Понятие тест-дублеров введено неким Gerard Meszaros в своей книге «XUnit Test Patterns». Джерард и Мартин делят все тест-дублеры на 4 группы:

Предположим, что вам нужно протестировать метод Foo() класса TestFoo, который делает вызов другого метода Bar() класса TestBar. Предположим, что метод Bar() принимает какой-нибудь объект класса Bla в качестве параметра и потом ничего особого с ним не делает. В таком случае имеет смысл создать пустой объект Bla, передать его в класс TestFoo (сделать это можно при помощи широко применяемого паттерна Dependency Injection или каким-либо другим приемлемым способом), а затем уже Foo() при тестировании сам вызовет метод TestBar.Bar() с переданным пустым объектом. Это и есть иллюстрация использования dummy-объекта в unit-тестировании.

Метод Bar() выполняет какие-то действия с ним (допустим, Bar() сохраняет данные в базу или вызывает веб-сервис, а мы этого не хотим). В таких случаях наш объект класса TestBar должен быть уже не таким глупым. Мы должны научить его в ответ на запрос сохранения данных просто выполнить какой-то простой код (допустим, сохранение во внутреннюю коллекцию). В таких случаях можно выделить интерфейс ITestBar, который будет реализовывать класс TestBar и наш дополнительный класс FakeBar. При unit-тестировании мы просто будем создавать объект класса FakeBar и передавать его в класс с методом Foo() через интерфейс. Естественно, при этом класс Bar будет по-прежнему создаваться в реальном приложении, а FakeBar будет использован лишь в тестировании. Это иллюстрация fake-объекта

Stub-объекты (стабы) – это типичные заглушки. Они ничего полезного не делают и умеют лишь возвращать определенные данные в ответ на вызовы своих методов. В нашем примере стаб бы подменял класс TestBar и в ответ на вызов Bar() просто бы возвращал какие-то левые данные. При этом внутренняя реализация реального метода Bar() бы просто не вызывалась. Реализуется этот подход через интерфейс и создание дополнительного класса StubBar, либо просто через создание StubBar, который является унаследованным от TestBar. В принципе, реализация очень похожа на fake-объект с тем лишь исключением, что стаб ничего полезного, кроме постоянного возвращения каких-то константных данных не требует. Типичная заглушка. Стабам позволяется лишь сохранять у себя внутри какие-нибудь данные, удостоверяющие, что вызовы были произведены или содержащие копии переданных параметров, которые затем может проверить тест.

Mock-объект (мок), в свою очередь, является, грубо говоря, более умной реализацией заглушки, которая уже не просто возвращает предустановленные данные, но еще и записывает все вызовы, которые проходят через нее, чтобы вы могли дальше в unit-тесте проверить, что именно эти методы вот этих вот классов были вызваны тестируемым методом и именно в такой последовательности (хотя учет последовательности и строгость проверки, в принципе, настраиваемая вещь). То есть мы можем сделать мок MockFoo, который будет каким-то образом вызывать реальный метод Foo() класса TestFoo и затем смотреть, какие вызовы тот сделал. Или сделать мок MockBar и затем проверить, что при вызове метода Foo() реально произошел вызов метода Bar() с нужными нам параметрами.

Unit-тестирование условно делится на два подхода:

То есть в state-based testing нас интересует в основном, в какое состояние перешел объект после вызова тестируемого метода, или, что более часто встречается, что в реальности вернул наш метод и правилен ли этот результат. Подобные проверки проводятся при помощи вызова методов класса Assert различных unit-тест фреймворков: Assert.AreEqual(), Assert.That(), Assert.IsNull() и т.д.

В interaction testing нас интересует прежде всего не статическое состояние объекта, а те динамические вызовы методов, которые происходят у него внутри. То есть для нашего примера с классами TestFoo и TestBar мы будем проверять, что тестируемый метод Foo() действительно вызвал метод Bar() класса TestBar, а не то, что он при этом вернул и в какое состояние перешел. Как правило, в случае подобного тестирования программисты используют специальные mock-фреймворки (TypeMock.Net, EasyMock.Net, MoQ, Rhino Mocks, NMock2), которые содержат определенные конструкции для записи ожиданий и их последующей проверки через методы Verify(), VerifyAll(), VerifyAllExpectations() или других (в зависимости от конкретного фреймворка).

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

Пример тестирования с использованием стаба для state-based тестирования:

Пара пояснений по коду. Сначала мы создаем объект типа Order, затем – стаб для класса Warehouse. После этого мы при помощи mock-фреймворка говорим, что при вызове метода HasInventory с определенными параметрами этот метод должен нам вернуть true. Аналогичным образом переопределяем поведение метода Remove (а то еще вызовет реальный и будет бяка). Далее идет вызов метода Fill() с переданным стабом, после чего проверяется, что свойство IsFilled установлено в true. Как видите, ничего сложного. Однако данный тест обладает некоторыми недостатками. Во-первых, непонятно, что делать, если в тестируемом объекте нет свойства, аналогичного IsFilled. Как проверять правильность выполнения кода? Во-вторых, непонятно, что случится, если программист удалит или закомментирует вызов следующей строчки в коде метода Fill():

IsFilled устанавливается в true, тест проходит, но код-то уже не работает!

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

Начало теста аналогичное, затем идет создание мока Warehouse, после чего идет несколько вызовов метода Expect с теми же параметрами, что и в предыдущем тесте. При помощи этого метода мы говорим моку, что мы ожидаем вызова этих методов с такими параметрами и нам в ответ на их вызовы нужно вернуть такие-то значения. Затем идет вызов метода Replay(), который переводит мок из режима записи ожиданий в режим их проверки, то есть запуска тестового метода. Все моки имеют несколько режимов работы (Record, Replay, Verify), это распространенный подход. Далее непосредственно запуск, проверка IsFilled и вызов нового для нас метода VerifyAllExpectations(). Последний как раз и делает всю работу по проверке вызовов методов, параметров и т.д. Теперь, если метод Remove оказался закомментированным, тест не пройдет. Кроме того, нам уже не так важна проверка состояния объекта Order. Если бы свойства IsFilled не было, ничего бы не изменилось, а так мы лишь проверяем, что оно было установлено в соответствии с алгоритмом. Теперь немного поэкспериментируем с кодом. Что, если мы уберем второй Expect или поменяем их местами? Есть несколько режимов строгости проверки, которые задаются через конструктор класса Mock, который также можно использовать для создания мока. В Rhino Mocks есть три уровня строгости: Loose, Strict и Default (Loose). В Loose-режиме мок проверяет лишь то, что все ожидаемые методы были вызваны из тестируемого метода, в то время как в Strict-режиме проверяется также, что не было любых других вызовов и что порядок вызову соответствует порядку ожиданий. В других фреймворках иногда есть и другие режимы. Таким образом, в нашем случае при изменении порядка тест бы прошел, но в Strict-режиме – уже нет. Еще один момент, который показывает отличие методов Expect от методов Stub (в моке они также доступны): методы, зарегистрированные в моке при помощи метода Stub невидимы для метода VerifyAllExpectations. То есть, если нужна проверка вызовов – используйте Expect. Также стоит отметить, что при помощи дополнительных методов типа Return вы можете не только указывать возвращаемые значения, но еще генерировать exception’ы (Throw), вызывать настоящий метод (CallOriginalMethod), задавать ограничения на параметры (Constraints), вызывать дополнительные методы (Callback, Do), работать со свойствами и событиями. В общем, список потрясающий.

Источник

Моки и стабы

Существует категория классов, которые тестировать весьма просто. Если класс зависит только от примитивных типов данных и не имеет никаких связей с другими бизнес-сущностями, то достаточно создать экземпляр этого класса, «пнуть» его некоторым образом путем изменения свойства или вызова метода и проверить ожидаемое состояние.

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

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

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

ПРИМЕЧАНИЕ
Не нужно бросать в меня камнями с криками «Да кто сегодня вообще такую хрень написать можно? Ведь уже столько всего написано о вреде такого подхода, да и вообще, у нас есть юнити-шмунити и другие полезности, так что это нереальный баян двадцатилетней давности!». Кстати, да, это баян, но, во-первых, речь не юнитях и других контейнерах, а о базовых принципах, а во-вторых, подобное «интеграционное» тестирование все еще невероятно популярно, во всяком случае, среди многих моих «зарубежных» коллег.

Создания «швов» для тестирования приложения

Даже если не задумываться о том, какое количество новомодных принципов проектирования нарушает наша вью-модель, четко видно, что ее дизайн несколько … убог. Ведь даже если проектировать старым дедовским бучевским методом, то становится понятно, что всю работу по сохранению имени последнего пользователя, логику по работе с базой данных (или другим внешним источником данных) нужно спрятать подальше с глаз долой и сделать это «проблемой» кого-то другого и использовать уже этого «кого-то» в качестве «строительного блока» для получения более высокоуровневого поведения:

Пока что написание модульного теста все еще остается затруднительным, но становится понятным, как можно достаточно просто «подделать» реальную реализацию класса LastUsernameProvider и сымитировать нужное для нас поведение. Достаточно выделить методы этого класса в отдельный интерфейс или просто сделать их виртуальными и переопределить в наследнике. После чего останется лишь «прикрутить» нужный нам объект в нашу вью-модель.

Даже не прибегая ни к каким сторонним библиотекам для «инджекта» зависимостей мы можем сделать это самостоятельно несколько простыми способами. Нужную зависимость можно передать через дополнительный конструктор, через свойство или создать фабричный метод, который будет возвращать интерфейс ILastUsernmameProvider.

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

Поскольку дополнительный конструктор является внутренним (internal), то он доступен только внутри этой сборке, а также «дружеской» сборке юнит-тестов. Конечно, если тестируемые классы являются внутренними не будет не какой, но поскольку все «клиенты» внутреннего класса находятся в одной сборке, то и контролировать их проще. Подобный подход, основанный на добавлении внутреннего метода для установки «фальшивого» поведения является разумным компромиссом упрощения тестирования кода, не налагая ограничения на использования более сложных механизмов управления зависимостями, типа IoC контейнеров.

ПРИМЕЧАНИЕ
Одним из недостатков при работе с интерфейсами является падение читабельности, поскольку не понятно, сколько реализаций интерфейса существует и где находится реализация того или иного метода интерфейса. Такие инструменты, как Решарпер существенно смягчают эту проблему, поскольку поддерживают не только навигацию к объявлению метода (Go To Declaration), но также и навигацию к реализации метода (Go To Implementation):

Что такое stubbing java. Смотреть фото Что такое stubbing java. Смотреть картинку Что такое stubbing java. Картинка про Что такое stubbing java. Фото Что такое stubbing java

Проверка состояния vs проверка поведения

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

Для нормальной реализации этих тестов нам нужна «фейковая» реализация интерфейса, при этом в первом случае, нам нужно вернуть произвольное имя последнего пользователя в методе ReadLastUserName, а во втором случае – удостовериться, что вызван метод SaveLastUserName.

Именно в этом и отличаются два типа «фейковых» классов: стабы предназначены для получения нужного состояния тестируемого объекта, а моки применяются для проверки ожидаемого поведения тестируемого объекта.

Стабы никогда не применяются в утверждениях, они простые «слуги», которые лишь моделируют внешнее окружение тестового класса; при этом в утверждениях проверяется состояние именно тестового класса, которое зависит от установленного состояния стаба.

У моков же другая роль. Моки «подсовываются» тестируемому объекту, но не для того, чтобы создать требуемое окружение (хотя они могут выполнять и эту роль), а прежде всего для того, чтобы потом можно было проверить, что тестируемый объект выполнил требуемые действия. (Именно поэтому такой вид тестирования называется behaviortesting, в отличие от стабов, которые применяются для statebasedtesting).

А зачем мне знать об этих отличиях?

Действительно, разница в понятиях может показаться незначительной, особенно если вы реализуете подобные «фейки» руками. В этом случае знание этих паттернов лишь позволит говорить с другими разработчиками на одном языке и упростит наименование фейковых классов.

Однако рано или поздно вам может надоесть это чудесное занятие по ручной реализации интерфейсов и вы обратите свое внимание на один из Isolation фреймворков, таких как Rhino Mocks, Moq или Microsoft Moles. Там эти термины встретятся обязательно и понимание отличий между этими типами фейков вам очень пригодится.

Я осознанно не касался ни одного из этих фреймворков, поскольку каждый из них заслуживает отдельной статьи и ИМО лишь усложнит понимание этих понятий. Но если вам все же интересно посмотреть на некоторые из этих фреймворков более подробно, то об одном из них я писал более подробно: “Microsoft Moles”.

Источник

Stubbing and Mocking in Java with the Spock Testing Framework

Что такое stubbing java. Смотреть фото Что такое stubbing java. Смотреть картинку Что такое stubbing java. Картинка про Что такое stubbing java. Фото Что такое stubbing java

Что такое stubbing java. Смотреть фото Что такое stubbing java. Смотреть картинку Что такое stubbing java. Картинка про Что такое stubbing java. Фото Что такое stubbing java

When it comes to true unit tests, having a mocking framework such as Spock for Java is essential. Using a mocking framework ensures that your unit tests are fast, self-contained and deterministic. A mocking framework can help you fake external systems, pre-program your classes with expected responses, and test hard-to-replicate error conditions.

There are several mocking frameworks in the Java world, but the most popular one is Mockito. Our previous mocking tutorial is a great introduction to Mockito if you would like a detailed tutorial on using it.

In this tutorial, we will cover the Spock testing framework, an emerging framework that can be used to test both Java and Groovy code. Unlike Mockito, Spock is a complete testing framework that can function on its own (Mockito works in addition to JUnit).

For the code examples, we will use the same situations that were presented in the Mockito tutorial. This will allow for easy comparison between the two frameworks. If you ever wanted to see how Spock competes against the Junit/Mockito combo, you will find this tutorial particularly interesting.

Notice that the tutorial will focus exclusively on testing Java code. Even though Spock can also work with Groovy code, in this tutorial we will focus on Java unit tests. Spock can test both Java and Groovy, but in the case of Groovy, it has some additional capabilities that we will not cover here.

Prerequisites

It is assumed that you already have a Java project and you want to write unit tests for it. We will need:

The Java project may or may not have existing JUnit tests. Spock tests can be added to the project without any negative impact on the existing JUnit tests. Both kinds of tests will run when the Maven test goal is executed.

Groovy knowledge is NOT required, as the Spock tests will be written in a Groovy style that is very close to Java. However, for some advanced examples, it would be beneficial to know Java 8 lambda expressions as they will be similar to the Groovy closures in this tutorial.

It is also assumed that we already know our way around basic Maven builds. If not, feel free to consult its official documentation first.

Mockito knowledge is not strictly required. We will cover some differences between Mockito and Spock that will be useful if you are already a Mockito veteran.

Setting Up Spock

Spock is already distributed via Maven central, so using it in a Java forward is a painless process. We just need to modify the pom.xml and add the following dependencies:

The reason why we need three dependencies instead of just one is that the extra libraries are needed to “replicate” some of the needed built-in Groovy functionality, in case if we wanted to write unit tests for a Groovy application.

In order to integrate Spock into the Maven lifecycle, we also need the following additions in the same pom file:

The Surefire plugin configuration ensures that both JUnit and Spock unit tests are honored by Maven.

Spock unit tests will need to be placed in the src/test/groovy/ folder, while JUnit tests (if any) are still in src/test/java/ as per Maven guidelines.

Spock and the Java Ecosystem

If you are already a veteran of JUnit and assorted testing tools, you might wonder why Spock was created in the first place. Aren’t the existing testing frameworks capable of dealing with all of our testing needs?

The answer is that Spock was created to cover the full testing lifecycle of a Java Enterprise application. The existing tools have a long history and come with several legacy features that cannot always keep up with newer testing practices. The most evident example of this is the fact that JUnit covers only plain unit testing and nothing else. If you need to mock some services or wish to employ Behavior-driven development, JUnit is simply not enough. You are forced to add more testing frameworks into the mix, each one with its own idiosyncrasies and issues.

Spock takes a step back and offers you all the testing facilities you might need during the full testing lifecycle. It comes with built-in mocking and stubbing and several extra testing annotations created exclusively for integration tests.

Что такое stubbing java. Смотреть фото Что такое stubbing java. Смотреть картинку Что такое stubbing java. Картинка про Что такое stubbing java. Фото Что такое stubbing java

At the same time, because Spock is a newer testing framework, it had the time to observe common pitfalls of the existing frameworks and either fix them or offer a more elegant workaround. Explaining all the advantages of Spock over existing solutions is out of the scope of this article. A more detailed comparison between Spock and the JUnit/Mockito combo can be found in the Spock vs JUnit article.

In the context of mocking, Spock offers four major advantages:

A Word on Mocking and Stubbing

The theory behind mocking and stubbing was already explained in the previous article under the section “The Need for Mocks and Stubs”. We are not going to repeat it here. In summary, we use mocks to isolate the class under test and examine it in a completely controlled environment. Common targets for mocking are:

We will cover two kinds of mock objects. Stubs are fake classes that come with preprogrammed return values. Mocks are fake classes that we can examine after a test has finished and see which methods were run or not.

Semaphore also provides tutorials for mocking in other languages if your interests go beyond Java:

Basic Stubbing with Spock

In our example application, we have a class that reads a customer from the database and forms their full name.

Here is the code for the customer:

and here is our business class:

This class reads customers from the database via the EntityManager. We will test it by stubbing the EntityManager so that we can decide what gets returned without having a real database.

Here is the Spock unit test:

Even if you have never seen Groovy/Spock before, the syntax of the unit test should be familiar, as the code is deliberately written to be Java-like.

First of all, we have named our unit test using a full sentence that explains its purpose (customer full name is first name plus last name). With Spock, you are free to use proper English explanations for your unit tests. These sentences also appear in unit test reports, so they are very valuable for other people in your organization (e.g. Project Managers), as they can understand what a unit test does with zero Java/Groovy knowledge.

More importantly, the test content itself is marked with given, and, when, then labels that showcase the BDD spirit of Spock. These labels are called blocks in Spock parlance, and they logically divide the unit test to create a well-defined structure. The strings next to each label serve as a human-readable explanation of the associated code block.

The idea is that somebody can focus only on these labels and understand what the unit tests dos without actually looking at the code. In this particular example, the following blocks are contained:

Reading the block descriptions creates an English sentence that serves as a mini-specification of what the test does. The labels can be normal strings, so you should strive to name them according to your business domain and abstraction depth that suits you. Ideally, you should use full sentences to explain what each block does in a high-level manner.

The given: block contains just some Java code that creates a sample customer. The first and: block is where we actually create a fake object. In this particular case, we create a stub using the static method Stub() that Spock offers. We essentially tell Spock that it will need to create a fake object for the EntityManager class.

The most important line of the whole test is the next one. The line entityManager.find(Customer.class,1L) >> sampleCustomer instructs Spock what to do when the find() method of the stub is called.

The caret character means “return” in Spock parlance. The whole statement says: “when the entityManager find() method is called with arguments Customer class and 1, return our sample customer”.

If you know how Mockito works, the equivalent line would be: when(entityManager.find(Customer.class,1L)).thenReturn(sampleCustomer);

We’ve now both created a Stub object with Spock, and also instructed it with a dummy return result. Next, we create our CustomerReader reader object and pass as a dependency the fake object. From now on, the CustomerReader class will function without understanding that the EntityManager is not a real one.

In the when: block, we call our test method in the usual Java manner. The final block is the then: block. This is the block that decides if the test will fail or not. Unlike Junit, Spock does not use assert statements. Instead, it expects normal boolean statements. Statements in the then: block will be evaluated by Spock as boolean, and if all of them are true, the test will pass. If any of them fail, the test will fail.

Notice also that no statement has a semicolon at the end of the line. Unlike Java, Groovy does not require semicolons.

With the test code in place, we can run this Spock unit test either from the command line (using the mvn test command), or via our favorite IDE. Here is an example with Eclipse.

As far as Eclipse is concerned, the Spock test is handled in exactly the same way as a JUnit test would be.

Что такое stubbing java. Смотреть фото Что такое stubbing java. Смотреть картинку Что такое stubbing java. Картинка про Что такое stubbing java. Фото Что такое stubbing java

The test result shows the title correctly as a full English sentence.

Grouping Multiple Unit Tests for the Same Class Under Test

In the previous section, we had a single unit test in a single file. In a real project, we will probably have multiple unit tests for the same class under test in order to evaluate multiple scenarios and conditions.

In those cases, it makes sense to move the stub creation process to a reusable method, removing code duplication. Similar to the @Before annotation in JUnit, Spock also allows the extraction of common setup code into a method that will run before each unit test.

You might have already noticed that our CustomerReader class is not correct, as it does not handle the null case, i.e. the given database ID does not exist as an object in the DB.

Let’s create a unit test that covers this scenario as well.

The method named setup() will be executed by Spock before each individual name. This functionality is detected by the name of the method itself (there is no Spock annotation for this).

Apart from extracting the common code for creating our test class and its mocked dependencies, we have also added a second scenario for the case when the customer is not in the database.

In true TDD fashion, we have created the unit tests before the actual implementation. If you run our unit test, the second test method will fail. Eclipse should still show the names of the tests as full sentences.

As an exercise, feel free to correct the CustomerReader implementation and also add extra unit tests when the first and/or last name are null themselves.

Что такое stubbing java. Смотреть фото Что такое stubbing java. Смотреть картинку Что такое stubbing java. Картинка про Что такое stubbing java. Фото Что такое stubbing java

To gain full advantage of the individual Spock blocks, you can also use the external project of Spock reports.

Here is a sample report:

Что такое stubbing java. Смотреть фото Что такое stubbing java. Смотреть картинку Что такое stubbing java. Картинка про Что такое stubbing java. Фото Что такое stubbing java

You can find information on how to use the reports in the README file.

Basic Mocking with Spock

In all the examples so far, we have only seen Spock stubs (i.e. classes that hardcoded return values). For a mocking example, let’s assume that we have the following class in our application:

This class has two external dependencies, a class that sends emails and an invoice database. It checks for late customer invoices and sends customers an email if an invoice is late.

We want to test the method notifyIfLate() and ensure that emails are sent to a customer only if they have outstanding invoices. Since the method does not have a return value, the only way to verify if it runs or not is to use a mock.

We need to test two scenarios. In the first one, the customer has an outstanding invoice (and thus an email should be sent). In the second one, the customer has no pending invoices. Here is the Spock code:

The second dependency – EmailSender is a bit different. At the end of the test, we need to know if an email was sent or not. The only way to do this is to check how many times the method sendEmail() was called using a mock.

Spock supports the creation of mocks using the static Mock() method. Here is the respective line:

When running a unit test, Spock keeps track of all the invocations that happen with mocked objects. At the end of the test, we can query these invocations with the following Spock syntax:

This line means: “after this test is finished, this method of mockedObject should have been called N times with these arguments “. If this has happened, the test will pass. Otherwise, Spock will fail the test.

The verification lines are placed inside the then: block of each test, right at the end of the test method.

In our case, the line 1 * emailSender.sendEmail(sampleCustomer) means: “once this test has finished, the sendEmail() method should have been called 1 time with the sampleCustomer class as an argument. This is the scenario when an email should be sent. In the second scenario, the number of times is zero because no email should be sent.

If you already know Mockito, these two Spock lines map to verify(emailSender, times(1)).sendEmail(sampleCustomer); and verify(emailSender, times(0)).sendEmail(sampleCustomer); respectively.

Spock is smart enough to monitor the expected invocations, and give a clear error message when things go wrong. For example, let’s assume that we have a bug in the code that does not send reminders to customers when they have outstanding invoices. The test will fail, and Spock will present the following error message:

Verifying Mocked Object Arguments

The previous example was relatively simple, we just verified whether a single method was called or not. Sometimes we need more detail, and instead of looking only at the method call itself, we are also interested in the arguments.

As an example, let’s assume that the analytics department wants more extensive metrics, and has asked you to implement an extra mechanism, where several important events for a customer are recorded and later analyzed.

Here is the source code of the Event that the metrics solution supports:

The LateInvoiceNotifier class is then augmented with an EventRecorder dependency and we want to write a unit test that verifies that the event recorded:

Here is the Spock test that not only verifies that the email event was recorded, but also checks its values:

Our class under test LateInvoiceNotifier now has 3 dependencies:

The creation of these 3 mocks happens in the setup() method. Our class under test now has 3 fake dependencies, and we have full control of all input and output that happens during the unit test.

If all the above are true, the test will succeed. If any of these statements is false, the whole test will fail. Spock is so smart that it can detect the difference between an invocation that has arguments that are similar, but not exactly the same.

Let’s assume that we have a bug in our application, and that the timestamp property is not correctly set. Spock will present the following test error:

Here, Spock tells us that, while our method was indeed called once, it was not called with the arguments we requested.

Forming Dynamic Responses for Mocks

The previous examples of Spock unit tests will cover most of your testing needs. We will now cover some advanced Spock examples that deal with dynamic manipulations of arguments and responses from mocks.

Again, Spock is based on Groovy closures for both of these features. If you know your way around Java 8 lambda expressions, then it should be very easy to follow.

However, keep in mind that dynamic manipulation of arguments and responses is an advanced technique that will be needed only for some very specific corner cases in your unit tests. If you have a choice, it is best to return predefined results in your mocks/stubs, so that the test is more readable. Use dynamic responses only as a last resort in your unit tests.

Dynamic Manipulation of Arguments with Spock

Let’s see an example where we just want to modify the argument itself. We’ll assume that you want to test the following class:

You should instantly see why writing a unit test for this class is a bit tricky. Even though the DAO logic itself is very basic, the fact that once the customer is saved using the persist method, its database ID is sent to the logger presents a problem. For this contrived example, the code will work just fine in the real system, as the database will indeed assign an ID to the object as soon as it is saved.

How can we replicate this processing in our unit test? The persist method does not return an argument, so we cannot mock it using Spock’s >> syntax.

Spock can still create a unit test for this scenario with the following test:

As with the previous unit tests, we create two fake objects in the setup() method:

The most important line of the whole unit test is the following:

Let’s break this line into two parts, the one before the >> operator, and the one after. The first part uses the underscore character as an argument. The underscore character is a special character in Spock, and it means “any”. It is used as an argument matcher that can match any value of the argument. The syntax as Customer is another special Spock construct that makes the test a bit more strict by ensuring that the argument is indeed a Customer class.

This is all that is needed to create dynamic responses in Spock. Mockito would need a special Answer construct here.

The last part of the test (the then: block) is the same as we have seen in previous examples. It just verifies that the info() method was executed once with the correct arguments and more specifically with the ID equal to 123 which was stubbed in the when: block.

Dynamic Responses Based on Arguments with Spock

As a grand finale, we will see an extreme unit test where the answer of a mock depends on the argument. Let’s assume that you want to test the following class:

Let’s say we want to test using a list of 20 customers. This time, the saveRepository method does return an argument, so in theory, we could stub that method with the >> operator 20 times to instruct it exactly what output it should send.

A more concise way is the following:

This Spock unit test essentially gathers all the techniques we have seen so far in one masterpiece. It combines simple stubbing, simple mocking, dynamic arguments and argument verification in the same file!

Let’s analyze everything in turn. Our class under test is MassUserRegistration and has two dependencies:

In our setup() method we stub UserRepository using dynamic arguments:

The first part before the >> operator matches the saveCustomer() method when any two arguments are passed by employing the underscore character. To make the test a bit more strict we make sure that the arguments are Strings (the as String ) syntax. If non-string arguments are passed, the unit test will fail.

The combination of these two parts translates to:

All these actions will be performed by Spock during the course of the unit test. Regardless of the size of our testing data, the UserRepository mock will always return the correct response to the class under test.

In the then: block we perform verification of the events for each customer. First of all, we use the Groovy each iteration that allows us to use a closure for each Customer. This is equivalent to the Java 8 forEach statement.

Notice that the unit test is written in such a way that the size of the input data is actually irrelevant. We can expand the test data from 20 customers to 100 customers, to 1000 customers, and the mocking and verification code will remain unchanged. This would not be the case if we had manually stubbed a different response for each specific customer.

Continuous Integration for Spock on Semaphore

Now that we have our tests, we can start doing continuous integration (CI). The first step is to integrate our project’s repository with Semaphore, which will wait for any pushes to our repository and will then run our tests for us.

Semaphore comes with an included stack of Java tools, including Maven and Gradle, allowing us to set up our project with minimal effort.

Run the Spock Example Project on Semaphore

To end the post on a practical note, let’s try the official Spock Example Project on Semaphore.

In this section, we’ll configure a CI Pipeline to build and test an application with Maven and Spock.

Что такое stubbing java. Смотреть фото Что такое stubbing java. Смотреть картинку Что такое stubbing java. Картинка про Что такое stubbing java. Фото Что такое stubbing java

Что такое stubbing java. Смотреть фото Что такое stubbing java. Смотреть картинку Что такое stubbing java. Картинка про Что такое stubbing java. Фото Что такое stubbing java

Что такое stubbing java. Смотреть фото Что такое stubbing java. Смотреть картинку Что такое stubbing java. Картинка про Что такое stubbing java. Фото Что такое stubbing java

Что такое stubbing java. Смотреть фото Что такое stubbing java. Смотреть картинку Что такое stubbing java. Картинка про Что такое stubbing java. Фото Что такое stubbing java

The main components of the builder are:

Что такое stubbing java. Смотреть фото Что такое stubbing java. Смотреть картинку Что такое stubbing java. Картинка про Что такое stubbing java. Фото Что такое stubbing java

The job uses some convenient commands provided by Semaphore:

Using the Cache

Our initial CI pipeline does its job, but we can improve it. We have to tweak it a bit to:

Let’s customize the pipeline:

Что такое stubbing java. Смотреть фото Что такое stubbing java. Смотреть картинку Что такое stubbing java. Картинка про Что такое stubbing java. Фото Что такое stubbing java

We have introduced some new concepts in this block:

To try the workflow:

Что такое stubbing java. Смотреть фото Что такое stubbing java. Смотреть картинку Что такое stubbing java. Картинка про Что такое stubbing java. Фото Что такое stubbing java

Add the Tests

Add a second block to run the tests:

Что такое stubbing java. Смотреть фото Что такое stubbing java. Смотреть картинку Что такое stubbing java. Картинка про Что такое stubbing java. Фото Что такое stubbing java

Что такое stubbing java. Смотреть фото Что такое stubbing java. Смотреть картинку Что такое stubbing java. Картинка про Что такое stubbing java. Фото Что такое stubbing java

Good job! You now have an optimized CI pipeline for your Java project. You can write your application with the confidence that the code is being tested on each step of the way. Also, Semaphore comes with a neat Test Reports feature that gives you a full overview of your test suite.

You can add more tests or even combine multiple tests framework by adding more jobs into the Tests block.

Once you’ve mastered Continuous Integration, you can go a step further and use Semaphore to Continously Deploy your application.

Summary

In this tutorial, we’ve written several unit tests using Spock for both stubbing and mocking.

Where to Go from Here

We’ve seen Spock’s most important mocking features, but there are several more to explore:

If you have any questions and comments, feel free to leave them in the section below. Also, feel free to share this tutorial with anyone you think might benefit from it.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *