(java || kotlin) && devOps
339 subscribers
14 photos
2 videos
7 files
429 links
Полезное про Java и Kotlin - фреймворки, паттерны, тесты, тонкости JVM. Немного архитектуры. И DevOps, куда без него
Download Telegram
Всем привет!

Пару слов про отладку тестов, на примере Maven проекта и IDEA.

Если речь про модульные тесты - там проблем нет, ставим breakpoint и запускаем нужный тест в IDEA.

Проблемы есть с интеграционными тестами. Т.к.
1) тесты и код, который тестируем, запускаются в разных процессах
2) обычно интеграционные тесты запускаются с помощью Maven (Gradle), т.к. нужно поднять окружение, и проще и правильнее эти настройки один раз сделать в Maven и забыть.

Какие у нас варианты с отладкой?

1) вариант, описанный тут https://stackoverflow.com/questions/49390110/how-to-debug-integration-test-in-intellij
Если вкратце - запускаем тесты с помощью Maven в специальном отладочном режиме плагина failsafe (или surefire), потом подключаемся к процессу из IDEA в режиме удаленной отладки. Отладка называется удаленной, т.к. нужно подключаться к другому процессу. В данном случае на localhost.

2) запускаем наш сервис в "DEV режиме" через Maven. Также, как это делается для отладки кода и проверки функционала локально вручную. Обычно под это дело в проекте есть отдельный профиль Maven. После этого запускаем нужный интеграционный тест в Debug режиме из IDEA, из окна с кодом теста. Как минимум, интеграционные тесты Cucumber и Spring Boot IDEA поддерживает.

3) а что, если нужно поставить точку прерывания в коде сервиса, а не тестов? Тогда настаиваем в IDEA Run configuration для нашего контейнера сервлетов \ сервера приложений. Запускаем ее в DEBUG режиме, после чего запускаем тесты также, как и в предыдущем варианте.

Варианты 2 и 3 мне нравятся больше, т.к. не нужно использовать консоль - все делается средствами IDEA.

#debug #java #tests #integration-tests
👍5
Всем привет!

Нашел хорошую статью о том, как совместить тестирование Spring контроллеров и один из самых известных фреймворков для тестирования REST - Rest Assured. https://www.baeldung.com/spring-mock-mvc-rest-assured

Кстати, в начале статьи есть ссылка на пример использования чистого Spring MVC Test, если кто его не использовал - можете сравнить синтаксис.

Еще статья хороша тем, что четко разделяет модульные и интеграционные тесты. И я бы разделил точно также) Я иногда задаю вопрос о видах тестов на интервью, ответ мне не всегда нравится. Для ленивых, вкратце - интеграционным тест можно считать, если появляется сеть - открывается порт, вызывается другой процесс, внешнее хранилище, пусть даже и в embedded варианте. Хотя справедливости ради - вопрос холиварный, из-за того, что много пограничных случаев.

#unittests #spring #rest #integration_tests #interview_question
Всем привет!

Продолжение про тесты, на этот раз интеграционные.
Доминирующий протокол для интеграции сейчас - 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
👍2❤1🔥1
Магические 80% покрытия кода тестами.

Часто возникает вопрос - почему 80?
Краткий ответ - почему бы и нет)

А если серьезно: оценка может быть числовой (процент покрытия) или бинарной (достаточно или нет).
Бинарная плоха тем, что не документирована и зависит от мнения эксперта. Это может привести к проблемам на код-ревью.
А тут умные люди из SonarQube придумали метрику покрытия https://docs.sonarsource.com/sonarqube-server/user-guide/code-metrics/metrics-definition#coverage
и дали её рекомендуемое значение. И это не 100%, т.е. запас есть.
Считаю, что от этой рекомендации стоит отталкиваться.
К тому же JaCoCo, обычно используемый для расчета покрытия, позволяет гибко настраивать сам процесс.

Следующий вопрос, который может возникнуть: "Почему ради достижения 80% я должен писать тесты на элементарный код?" Я про getter, setter и все, все, все https://t.me/javaKotlinDevOps/34
Ответ: "Нет, не нужны эти тесты".
Лишний код можно исключить из покрытия:
1) generated код исключается по умолчанию начиная с JaCoCo 0.8.2
2) generated код Lombok - через lombok.addLombokGeneratedAnnotation = true (жаль, что только для Lombok это можно сделать, но есть еще альтернатива - см. следующий абзац.)
3) тривиальный код - явно исключая классы или пакеты по маске через настройки JaCoCo. Если какой-то код не получается исключить таким образом - это повод задуматься о рефакторинге.

Еще вопрос: "Зачем писать модульные (unit) тесты ради покрытия на интеграционный код: сервисные классы или классы контроллеров, которые представляют линейную цепочку вызовов методов?"
Ответ: "Не обязательно писать unit тесты".
Для простых микросервисов является нормальной перевернутая пирамида тестирования - когда интеграционных тестов разработки больше, чем модульных.
Простой код можно покрыть интеграционными тестами, включив их в общее покрытие. JaCoCo позволяет это настроить. Единственная проблема может быть, если тесты находятся в одном модуле, а код для покрытия - в другом, но и она решается.

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

Ну и если ничего не помогло - тогда уже стоит задуматься о новом Quality Gate в SonarQube с меньшим процентом покрытия.
Никакой магии нет - https://t.me/javaKotlinDevOps/403.
Но числовой показатель (80% по умолчанию), задокументированные исключения и цель покрыть максимум кода, в котором возможны ошибки, быть должны.

P.S. Еще AI можно попросить тесты написать, у него это неплохо получается)

#unittests #integration_tests #java
Когда я впервые увидел embedded Kafka, то подумал: Круто, жаль, что такого же нет для PostgreSQL.
И был не прав, спасибо, коллега просветил.
Встречаем Zonky Embedded Postgres https://github.com/zonkyio/embedded-postgres

Подключается как Java зависимость. Можно управлять версией PostgreSQL. Поддерживает ОС Darwin, Windows, Linux, Alpine Linux и Intel и ARM архитектуры. Легко подключается в тесты и утилиты миграции БД через rules.

@Rule
public SingleInstancePostgresRule pg = EmbeddedPostgresRules.singleInstance();


@Rule
public PreparedDbRule db =
EmbeddedPostgresRules.preparedDatabase(
LiquibasePreparer.forClasspathLocation("liqui/master.xml"));


Также можно работать напрямую с объектом БД:

try (EmbeddedPostgres pg = EmbeddedPostgres.start();
Connection c = pg.getPostgresDatabase().getConnection()) {
...
}


Как по мне - крутая штука, и вот почему. Рассмотрим альтернативы:
1) обычная инсталляция PostgreSQL - есть момент с правами, плюс единая инсталляция на компьютере разработчика - это shared ресурс на несколько проектов и риск поломки БД
2) Docker - опять же может не быть необходимых прав, плюс Docker - это тонкая, но все же прослойка. Которую нужно запустить и которая немножко жрет ресурсы.

В нашем случае по факту Постгря ставится в папку target/build, т.е. есть изоляция по проектам, плюс переустановка делается обычным clean.

Ну и отсюда к следуют минусы - на скачивание и распаковку в папку target нужно время.
Ну и время жизни такой инсталляции ограничено.
Поэтому идеальный кейс - тесты на живой БД.

#db #postgrrsql #integration_tests
❤1