(java || kotlin) && devOps
368 subscribers
6 photos
1 video
6 files
314 links
Полезное про Java и Kotlin - фреймворки, паттерны, тесты, тонкости JVM. Немного архитектуры. И DevOps, куда без него
Download Telegram
Всем привет!
Не JUnit-ом единым...
Если говорить о фреймворках для unit тестирования все наверняка вспомнят JUnit и Mockito. Но есть ещё много чего полезного в этой области. Сегодня расскажу про библиотеки для улучшения читаемости проверок - assert. Про важность читаемости кода, в т.ч тестового я уже писал.
Пример для затравки из AssertJ
assertThat(testList).hasSize(2)     .containsExactly("index3", "index4")     .isUnmodifiable();
Больше примеров см по ссылкам в конце поста.
Библиотек три.
1) Hamcrest. Старичок, родоначальник жанра, уже не развивается, не рекомендуется к использованию
2) AssertJ - в отличие от hamcrest построен на принципе method chaining, что позволяет использовать автопополнение IDE и выглядит более читаемо. Выводит более понятное сообщение об ошибке, что тоже важно. Есть фича Soft Assertion, позволяющая лениво описать n проверок и выполнить их за раз.
3) Truth - очень похож по принципу работы - method chaining - на AssertJ, при этом менее известен. В качестве преимущества его разработчики указывают более компактное API и более понятное логирование ошибок.
Как AssertJ, так и Truth позволяют создавать свои проверки.
За деталями предлагаю пойти сюда:
https://dzone.com/articles/hamcrest-vs-assertj-assertion-frameworks-which-one

https://habr.com/ru/post/675778/

https://truth.dev/comparison.html
#unittests #rare_test_libs
👍2
Всем привет!

Сегодня хочу рассказать про полезную библиотеку для Unit-тестирования - Instancio.
Ее цель - заполнение произвольными данными тестовых объектов. По умолчанию - произвольными и not null.
Но можно настройть селекторы для выбора определенных полей по маске (регулярке) и заполнении их данными по определенному алгоритму, определенными константами или null. Умеет заполнять коллекции и вложенные объекты.
Плюса я вижу два:
1) один очевидный - убрать boiler-plate код из тестового кода
2) второй не такой очевидный - при создании тестовых объектов руками часто во всех тестах используются одни и те же константы. Очевидные, типовые. Есть вероятность не попасть с выбранной тестовой константой в какие-то ветки тестовой логики, и т.об. не протестировать часть логики. Instancio же генерирует произвольные данные и может при очередном запуске помочь поймать редкую ошибку до того, как она проявится на ПРОМе.

Вот неплохая статья с примерами использования https://www.baeldung.com/java-test-data-instancio

#unittests #rare_test_libs
👍5
Всем привет!

Была у меня цель - сделать цикл постов с рассказом про малоизвестные тестовые библиотеки. Как говорится не JUnit-ом и Mockito едиными жив разработчик) Пару постов я уже сделал - Truth и Instancio.
Сегодня хочу рассказать про библиотеку для тестирования результата выполнения асинхронного процесса - Awaitibility.
https://github.com/awaitility/awaitility/wiki/Usage#usage-examples
На самом деле принципиальной проблемы протестировать хорошо спроектированный асинхрон нет. Нужно только найти, что меняется в системе после выполнения и проверить это значение. Как передать это значение в тест - проблема проектирования "боевого" кода, а не теста.
А для ограничения времени ожидания завершения асинхронного процесса в JUnit 5 есть методы assertTimeout() и assertTimeoutPreemptively().

Но есть ряд интересных фичей, которые позволяет настроить Awaitibility:
1) не только максимальное, но и минимальное время для проверки условия успешного выполнения теста
2) время, в течение которого тестируемая переменная должна сохранять ожидаемое значение
3) поток или пул потоков, в котором нужно проверить значение ThreadLocal
4) проброс исключений из других потоков в тест и игнорирование определённых исключений
5) алгоритм и интервал pulling-а значения тестируемой переменной (!)
6) не и наконец есть возможность указать failFast условие - при наступлении которого ожидание результата завершится досрочно с ошибкой. Это в целом синтаксический сахар, но кажется полезным учитывая длительность тестов асинхронного кода.

Пример кода:
await()
.atLeast(1,SECONDS)
.and()
.atMost(2, SECONDS)
.during(800, MILLISECONDS)
.failFast("Order should never be considered paid when it's voided",  () -> assertThat(orders.findById(1234).state).isEqualTo(PAID))
.until(() -> orders.findById(1234).state == VOIDED);

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

P.S. Тэг для этой темы
#rare_test_libs
👍5
Всем привет!

Продолжим про тестовые библиотеки. Достаточно часто возникает задача проверить в тесте структурированные текстовые данные. Я про XML, json и yaml как самые распространённые варианты. XML первым указан по старшинству, а не распространённости) И он пока ещё жив.

Зачем для этого нужно специализированная библиотека:
1) текст может отличаться формированием - пробелами и переносами строк. Иногда это важно при сравнении, но часто нужно проигнорировать
2) могут быть «лишние» тэги/атрибуты - которые не должны участвовать в сравнении
3) может отличаться порядок тэгов
4) может стоят задача проверить только отдельные элементы в дереве, или их количество

Одной библиотеки для всех форматов нет, но есть две - XMLUnit и JsonAssert. Так думал я, пока не начал копать тему глубже. И искать, чем же можно проверить yaml. Оказывается, есть «более лучшая» замена JsonAssert, которая:
1) умеет и в json, и в yaml, причём может сравнивать json и yaml. И также сравнивать с Java обьектом
2) умеет все это делать в стиле Assert/Truth, он же method chaining. А это облегчает как написание условий проверки благодаря auto competition в IDE, так и их чтение. А возможно в некоторых случаях позволит отказаться от BDD фреймворка.
3) прозрачно работает с разными источниками данных - строка и файл
Встречайте - ModelAssert https://github.com/webcompere/model-assert
И более подробно https://www.baeldung.com/json-modelassert
Что интересно - автор сам написал статью на baeldung и ссылается на неё в документации.
Ещё важный момент - подчеркивается совместимость библиотеки со Spring MockMvc и Mockito. Возможно даже ради этой поддержки автор и запилил совместимость с Hamcrest. И последнее - отдельно продумано тестирование json с guid. Во-первых можно игнорировать различия конкретных значений uuid (они могут генерироваться в каждом тесте), во-вторых легко написать проверку формата uuid.

А что же XML - вот хорошая статья по XMLUnit https://www.baeldung.com/xmlunit2
Он может примерно то же самое, но без method chaining. Но зато с валидацией по схеме (xsd). Что кстати неплохо характеризует отличия в подходах к работе с XML и json)

#unittests #rare_test_libs #XML #json #yaml
👍5🔥2
Всем привет!

Продолжение про тесты, на этот раз интеграционные.
Доминирующий протокол для интеграции сейчас - http(s). REST, GraphQL, разные кастомные реализации. Если мы получаем данные по http - значит где-то есть http client. Он куда-то стучится. Как можно протестировать эту часть интеграционной цепочки?
На первый взгляд, самый простой вариант - развернуть в TestContainers реальный экземпляр сервиса поставщика. Но у нас же микросервисы, а значит он, возможно, вызывает ещё кого-то. И т.д. Даже если не вызывает - ходит в БД.
Поэтому для теста, да и для отладки, нужна заглушка. Кстати, в большинстве случаев это может быть одна и та же заглушка.
Заглушка может быть standalone и встроенная. Я за встроенную, т.к. удобно держать в одном репозитории с кодом и сам код, и все необходимые настройки для его отладки и работы. Кроме стендозависмых конечно.
Что нас нужно от заглушки:
1) возможность старта на произвольном порту и передачи этого порта в код (сервер всегда localhost). Захардкодить порт можно, но тесты запускаются на CI конвейере, в т.ч. на используемом совместно сервере. И вообще не должны зависеть от среды запуска.
2) удобная возможность задавать разные ответы для разных path, например, method chaining
3) учёт параметров в URL
4) возможность задать http код ответа
5) возможность задать http заголовки ответа
6) возможность эмулировать таймаут
Ну и в идеале простая инициализация в тесте, например, с помощью аннотации.

И вот наконец кандидаты.
1) Wiremock. Главные плюсы - большое число возможностей, накопленных за время существования, и возможность работы как во встроенном режиме, так и standalone. Примеры фишек Wiremock - сценарный режим, когда можно задать ответ в зависимости от предыдущего запроса. Встроенный генератор случайных данных в полях ответа. Возможность использовать данные запроса в ответе (templating). Статья как подружить Wiremock с тестами https://www.baeldung.com/spring-webclient-wiremock-integration-testing
2) @RestClientTest - это «магическая» аннотация Spring Boot, автоматически связывающая добавленный в класс теста RestTemplate с сервером заглушки. Т.е. настройку порта делать не нужно. Вот статья по использованию https://www.baeldung.com/restclienttest-in-spring-boot
Если используете Spring и RestTemplate или его наследника RestClient - наверное оптимальный вариант. Для WebClient - асинхронщины и реактивщины - не годится, соответствующий тикет был отклонен командой Spring https://github.com/spring-projects/spring-boot/issues/8404
3) А рекомендует Spring для WebClient использовать MockWebServer. Вот статья https://howtodoinjava.com/java/library/mockwebserver-junit-webclient/
Из интересных фишек я заметил возможность получения информации о последнем запросе, пришедшем на сервер. И простейший сценарный режим, когда можно настроить очередь ответов для ряда последовательных запросов. Настраивать сложнее, чем предыдущие два варианта.

Итого: рекомендую @RestClientTest для синхронного Spring и Wiremock для остального.

#integration_tests #rare_test_libs
👍21🔥1
Всем привет!

Продолжим тему тестирования http взаимодействий. Кроме http клиента приложение может выставлять API. Этот пост про REST API, как наиболее распространённое. В мире победившего Spring endpoint обычно создаётся с помощью Spring @RestController, но в теории может быть и JAX-RS контроллер или что-то другое, даже «голый» сервлет.

Можно рассмотреть 4 случая.

1) модульное тестирование слоя контроллеров Spring приложения. В этом случае все сервисы в контроллере «мокаются», а Spring context загружается в ограниченном объёме, необходимом для эмуляции контроллера. Это кстати крутая фишка Spring - эмуляция контроллера без реального сетевого взаимодействия для модульных тестов. Ключевые слова - MockMvc и @WebMvcTest. Хорошее описание тут: https://sysout.ru/testirovanie-kontrollerov-s-pomoshhyu-mockmvc/, надо смотреть п. 2
Как я уже писал ранее - встроенные assert-ы MockMvc для json можно при желании заменить на ModelAssert, см. мой пост https://t.me/javaKotlinDevOps/343

2) интеграционное тестирование всех слоёв Spring приложения, но без сети. Вообще говоря, что считать интеграционным тестом - отдельная большая тема, но в большинстве статей, что я видел, такие тесты называются интеграционными. Используется тот же MockMvc, но уже с @SpringBootTest. Основное отличие - загружается весь Spring Context. Поэтому тест медленнее, чем больше приложение, тем медленнее. Если надо - в контексте можно поменять бины для тестового режима через Spring Profiles. Пример такого текста - в статье выше см. п. 1

3) интеграционное тестирование Spring приложения с сетью. Используется @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) и TestRestTemplate. Сервер для тестов запускается на произвольном порту, порт можно получить в тесте. Тесты этого типа ещё медленнее. Когда может быть полезен - нужно приближённое «к бою» тестирование. Или какие фишки http протестировать, типа редиректа. Вот пример https://sysout.ru/testirovanie-spring-boot-prilozheniya-s-testresttemplate/
А вот сравнение всех 3 подходов https://www.baeldung.com/spring-mockmvc-vs-webmvctest

4) у нас не Spring контроллер. Или уже есть опыт использования RestAssured, и он сугубо позитивный) Или нужны какие-то фишки RestAssured. А их много - xsd/json валидация, проверка длительности выполнения, встроенное логирование запроса и ответа, в т.ч. условное - в случае ошибки, использование Groovy для получения данных из тела запроса (!). Также данная библиотека рекомендуется для BDD тестов, т.к придерживается принятой там терминологии Given-When-Then https://en.m.wikipedia.org/wiki/Given-When-Then Для выполнения теста приложение нужно вначале запустить, например, через фазу pre-integration-test цикла сборки Maven. Вот неплохой tutorial https://www.baeldung.com/rest-assured-tutorial

Итого - как всегда в Java есть хорошие инструменты для разных задач.

#unittests #integration_test #rare_test_libs
👍1🔥1
Что заменит AI?

Нашел очень хороший пример. Есть такая библиотечка - Datafaker https://www.datafaker.net/. faker - это обманщик если что) Генерирует правдоподобные тестовые данные: имена, улицы и т.д. с помощью разных провайдеров https://www.datafaker.net/documentation/providers/
Полезная штука. Сказал бы я год или два назад. А сейчас смотрим на историю версий https://www.datafaker.net/releases/2.4.2/ и видим, что в 2025 году что-то случилось. Новые версии перестали выходить.

Кейс очень похож на генерацию каркаса приложения https://t.me/javaKotlinDevOps/383, о которой я уже писал. Т.к. закодить можно счетное число вариантов генерации, а в LLM мы можем считать в неком приближении хранятся данные обо всем. Плюс она может что-то генерировать новое исходя из похожести данных. Хотя, последнее может как быть полезным, так и являться галлюцинацией)

#ai #rare_test_libs #unittests
Заглушка может стать умной

Я уже писал про заглушки в Java для отладки и тестов - https://t.me/javaKotlinDevOps/344
Там чемпионом по функционалу был WireMock.
Собственно для Java разработчика он им и остается, но есть и альтернатива, пришедшая из мира Go - HoverFly https://docs.hoverfly.io/en/latest/pages/keyconcepts/modes/modes.html
Но имеющая при этом native binding для Java https://docs.hoverfly.io/en/latest/pages/bindings/java.html

Вообще если их сравнивать по основным параметрам:
1) возможность гибкой настройки симуляции и проксирования запросов (spy и mock в терминологии Mockito)
2) захват ответов (capturing)
3) гибкое сопоставление запросов и ответов (request matching)
4) гибкая шаблонизация ответов (response templating): доступ к параметрам запроса, в т.ч. xpath, jsonpath, генерация случайных данных
5) сценарный режим = использование состояния в ответах (stateful mode)
6) проверка запросов в стиле Mockito, пример: verify(exactly(5), postRequestedFor(urlEqualTo("/many")))
7) в целом DSL в стиле Mockito
8) различные источники данных для заглушек: код, json, внешний сервер
9) проксирование запросов прямое и обратное
то будет примерный паритет.

В чем же разница, и зачем я пишу этот пост?

Сначала про плюсы WireMock:
1) Java экосистема и, следовательно, все фичи доступны в Java
2) embedded режим, в котором нет лишних процессов
3) больше встроенных возможностей по симуляции ошибок
4) ориентация на разработчика

Но у HoverFly есть свои "фишки":
1) работа в forward proxy режиме из коробки. Напомню про forward proxy - это то самое окошко в браузере, где можно указать через какой прокси должен проходить весь трафик. Известная реализация - Charles proxy, популярна среди мобильных разработчиков для отладки без бэкэнда. Плюсы такого режима проявляются на тестовых стендах - прозрачный для клиента перехват трафика. WireMock так тоже умеет, но это для него не основной режим https://wiremock.org/docs/proxying/#running-as-a-browser-proxy
2) middleware - к HoverFly можно создавать и подключать свои модули, модифицирующие запросы или генерирующие ответы https://docs.hoverfly.io/en/latest/pages/keyconcepts/middleware.html К сожалению, не на Java. Казалось бы модифицировать поведение заглушки можно в своем коде? Да, но ключевой момент middleware - это простая модульная система, т.е. стандартизация и переиспользование модулей.
3) упор на захват трафика, который выражается в наличии таких интересных режимов https://docs.hoverfly.io/en/latest/pages/keyconcepts/modes/modes.html, как Diff - сравнение реального трафика с заглушкой и Modify - реализует Man in the Middle, т.е. замену трафика на ходу, но не для злоумышленика, а для тестовых целей. Возможности по подмене трафика есть и у WireMock https://wiremock.org/docs/proxying/#remove-path-prefix, но их сильно меньше.
4) наличие CLI API - https://docs.hoverfly.io/en/latest/pages/reference/hoverctl/hoverctlcommands.html

Итог - видится, что HoverFly стоит рассмотреть автоматизаторам тестирования и нагрузочным тестировщикам, и иметь в виду - Java разработчикам.

Еще интересный итог - разработчики и автотестеры используют достаточно много общих инструментов - JUnit, WireMock\HoverFly...

#rare_test_libs #mocks