Упрощаем тестирование и программирование с Groovy DSL

Сложный страшный код для создания тестовых данных

Тут я даже не стал показывать пример заполнения каких-либо списков, это просто превращается в муку. Но я же не поплакать этот пост пишу!

Ищем решение

Я думаю многие писали такое и не испытывали положительных эмоций. Есть конечно другой вариант — написать простой билдер в стиле Java:

Уже намного лучше! Мы даже смогли без боли в нижней части позвоночника сформировать список Repairs. Но появились специфичные проблемы:

  • Потеряли смысл аттрибутов, например что такое «487511577896511» в crashRepair совсем непонятно
  • Мы начали использовать громоздкие конструкции setAttributeValue
  • Для поддержки такого решения требуется множество статических функций: car(..), crashRepair(..), machineComponents(..), scheduledRepair(..)

Я думаю многим хотелось бы видеть тестовые данные вот в таком виде:

Вот это то, что мы искали! Прекрасный пример с минимальной избыточностью и прекрасной структурой!

DSL и Groovy

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

Начнём с основ, с замыканий (Closure). Это объекты, по сути являющиеся функциями, которые имеют контекст выполнения. В них возможно использовать внешние переменные и методы вызывающего кода. Для этого у замыкания есть свойство delegate. Фактически это объект, у которого запрашиваются недостающие функции и переменные. Вот таким нехитрым образом можно заставить замыкание использовать наш объект для вызовов:

И можно использовать например так:

Здесь в замыкании вызывается метод plateNumber у объекта car, в который передаётся строка ‘IDDQD’.

Не супер конечно, но уже есть идеи как сделать конфетку.

И теперь мы видим уже вполне симпатичный код:

Ну и полный пример для ленивых:

Думаю в работе это может пригодиться, ведь даже в Java проектах многие леняться использовать Builder для тестов. Хотя многие ленятся писать тесты, ну тут уж я им ничем не помогу.

P.S. ещё в качестве исходных данных можно использовать объекты в сериализованном виде: JSON или XML, в использовании не очень удобно, но иногда этого хватает. Только стоит помнить, что интроспекции IDE ничего не смогут проверить в ваших данных. А если вы ищете статически типизированное решение, то лучше паттерна Builder ничего нет.

Yuriy Artamonov on GithubYuriy Artamonov on LinkedinYuriy Artamonov on Twitter
Yuriy Artamonov
Software Developer
До последнего времени Юрий принимал активное участие в разработке опенсорс-фреймворка CUBA Platform, специализируясь на архитектуре и фронтенд-технологиях. Преподавал в Самарском университете разработку приложений для мобильных устройств, основы UI/UX и менторил студентов.

В настоящее время работает в компании JetBrains в команде IntelliJ IDEA Ultimate. Когда выдаётся свободное время, пишет статьи и контрибьютит в проекты с открытым исходным кодом. Обожает реализовывать странные идеи с лозунгом: «А почему бы и нет?».