(java || kotlin) && devOps
366 subscribers
6 photos
1 video
6 files
308 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
Всем привет!

Давно хотел написать про паттерны/шаблоны программирования. Основной вопрос, возникающий при разговоре про паттерны - какая от них польза? Ведь главное - умеет человек кодить или нет.
С одной стороны паттерны - это лишь часть арсенала программиста. Можно заучить все паттерны, но не научиться кодить.
И тут возникает второй вопрос - о каких паттернах мы говорим?
1) самые известные - паттерны проектирования из книги «банды четырёх» https://refactoring.guru/ru/design-patterns/catalog
Это синглтон, фабричный метод, билдер и все все все
2) паттерны Enterprise архитектуры от Фаулера https://martinfowler.com/eaaCatalog/
3) паттерны рефакторинга https://refactoring.com/catalog/ Про них также говорится в книге Идеальная работа Мартина
4) паттерны модульных тестов http://xunitpatterns.com/ и снова в книге Идеальная работа
5) паттерны интеграции корпоративных приложений https://www.enterpriseintegrationpatterns.com/patterns/messaging/toc.html многие из которых можно встретить в стандарте JMS
6) паттерны микросервисных приложений https://microservices.io/patterns/index.html
7) даже у Kubernates есть паттерны https://www.redhat.com/cms/managed-files/cm-oreilly-kubernetes-patterns-ebook-f19824-201910-en.pdf
8) не говоря уже про антипаттерны https://javarush.ru/groups/posts/2622-chto-takoe-antipatternih-razbiraem-primerih-chastjh-1
9) 10) ...
Из этого списка можно сделать вывод, что паттерны могут быть везде. А из этого второй вывод: паттерны - это удобный способ описания какой-то области разработки. Собственно это и есть их ценность. Шаблоны помогают изучить новую технологию, читать статьи, книги и главное читать код и тесты. Ну и проектировать систему, обсуждать ее архитектуру с коллегами. По сути паттерны - это язык проектирования. А идеальный способ их использования - когда они уже реализованы в неком фреймворке: Singleton и MVC в Spring, Builder в Lombok, Sidecar в k8s, или в языке как Singleton и Decorator в Kotlin.

#patterns #refactoring #unittests
Всем привет!

По следам отличной книги Владимира Хорикова Принципы юнит-тестирования, см https://t.me/javaKotlinDevOps/50, решил погрузится в тему и прочитать еще и классику жанра - Шаблоны тестирования xUnit. Рефакторинг кода тестов https://www.litres.ru/dzherard-mesarosh/shablony-testirovaniya-xunit-refaktoring-koda-t-48637685/
К слову, на английском книга выложена в виде сайта со списком паттернов тестирования http://xunitpatterns.com/
По сути книга и является большим справочников паттернов. Автор - человек дотошный: любую типовую практику, используемую для написания тестов он вынес в шаблон с подробным описанием истории возникнования, вариантов релизации и примеров, мотивирующих на использование шаблонов.
Вот примеры шаблонов из книги, на которые хотел бы обратить внимание:
1) 5 видов моков http://xunitpatterns.com/Mocks,%20Fakes,%20Stubs%20and%20Dummies.html - от Dummy object, который ничего не возвращает, не делает и не проверяет, а нужен лишь для передачи как параметр, до Fake Object - облегченной реализации боевой логики, без обращений во внешние системы и БД. Видел ссылки на эти 5 типов в других статьях и книгах.
2) Test Utility Method - одна из базовых практик при рефаторинге тестов, вынесение повторяющего кода в отдельные методы. Это может быть код настройки системы, код проверки результата с assert-ми и код очистики ресурсов. Очень полезная штука, с какого-то момента после рефакторинга вынесенных методов для их переиспользования мы получаем некий тестовый язык, по сути свой DSL, и это без всяких Cucumber-ов и прочих BDD. http://xunitpatterns.com/Test%20Utility%20Method.html

Остальное не влезло в пост телеги, см. на дзене https://dzen.ru/media/id/62fe543369f7d3314503a87d/recenziia-na-shablony-testirovaniia-xunit-6367bbba7d9dc52b40aa3ff3

#unittests #books
Всем привет!

Я подозреваю почти все знают и используют такой фреймворк, как Mockito. По крайней мере судя по собеседованиям, которые я провожу и коду, который вижу)
Наверняка все знают mock, spy, when(...).thenAnswer.
Но я хотел бы рассказать про ряд его малоизвестных особенностей:
1) можно создавать тестовые двойники через Mockito.mock или Mockito.spy, а можно воспользоваться аннотациями над полями класса: @Mock и @Spy, главное не забыть вызвать MockitoAnnotations.openMocks(testClass); перед каждым вызовом тестового метода
2) Mockito с версии 2 научился mock-ать final методы и классы
3) Mockito также научился mock-ать static методы. Что ж, PowerMock теперь не нужен) Да, остаются private методы, но во-первых я бы их не тестировал, а во-вторых если очень надо - есть рефлексия
4) через verify можно делать сложные проверки поведения: что методы какого-то класса не вызывались, что вызывались только определенные методы класса и ничего более, сколько раз вызывался метод, с какими аргументами. Здесь главное не переборщить и не сделать тест слишком хрупким. Разделять тестирование бизнес-процесса и детали реализации
5) с помощью ArgumentCaptor можно захватить содержимое передаваемого в mock параметра и проанализировать его отдельными assert-ами. Полезно для сложных объектов или для анализа содержимого строки
6) как известно, двойники, созданные с помощью mock, по умолчанию не возвращают ничего при вызове метода, т.к. вызова не происходит. Можно захардкодить ответ в помощью Mockito.when(...).thenReturn. Но если нужно вернуть значение в зависимости от входных параметров, а настраивать реальный объект не хочется - есть два варианта: создать наследника интерфейса Answer, где в методе answer сформировать ответ, либо сделать то же самое c использованием Streams API и Mockito.when(...).thenAnswer
Когда искал ссылки для поста, нашел отличную статью, где описываются практически все описанные выше лайфхаки: https://habr.com/ru/post/444982
Ну разве что кроме mock static https://www.baeldung.com/mockito-mock-static-methods и возврата сложного ответа из mock при помощи Answer https://www.baeldung.com/mockito-mock-methods

Пост будет дополняться полезными фичами по мере их нахождения)
Как говорить читать не перечитать https://www.javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html
Stay tuned)

#unittests #mockito
Всем привет!

Надеюсь все уже перешли на JUnit 5?

У JUnit 5 много крутых фич по сравнению с 4-й версией, например:
1) более удобная работа с параметризованными тестами через @ParametrizedTest, который вешается на метод - можно миксовать параметризированные и непараметризированные тесты в одном тестовом классе
2) возможность параметризировать не только входные параметры, но и все остальное в тесте с помощью динамически генерируемых тестов DynamicTests https://www.baeldung.com/junit5-dynamic-tests
3) есть возможность проверки исключений и таймаута через методы - assertThrows и assertTimeout. Это более правильно - assert идет после Act (я про Arrange-Act-Assert) и позволяет делать несколько проверок в одном тестовом методе
4) условное выполнение тестов в зависимости от среды: @EnabledOnOs, @EnabledOnJre, @EnabledIfSystemProperty, @EnabledIfEnvironmentVariable, также можно создавать свои условия
5) @RepeatedTest для повтора теста N раз
6) @DisplayName - полезно для задания говорящего имени теста для параметризированных тестов, к примеру
7) @Nested для складывания тестовых классов в "матрешку". Полезно, если у часть тестов нужно выполнить отнаследовавшись от базового класса
8) assertAll - объединение нескольких assert-ов в тесте. Полезно, как способ отключить fail fast режим проверок в JUnit - прогнать все assert-ы в тесте несмотря на падение одного из них.
9) ну и наконец механизм extensions. В 4-й версии был механизм Runner-ов, но его ограничение состояло в том, что на класс можно было повесить один Runner. А extensions может быть сколько угодно. Вот пример реализации BDD с помощью extension https://www.infoq.com/articles/deep-dive-junit5-extensions/ Здесь же есть пример 3-го способа параметризации теста - через ParameterResolver.
10) кроме всего прочего механизм extension добавляет store - контекст для обмена данными между тестами. Важно: для модульных тестов это антипаттерн, но нормально для интеграционных https://junit.org/junit5/docs/5.0.0/api/org/junit/jupiter/api/extension/ExtensionContext.Store.html
11) указание очередности тестов через @Order - полезно для тестирования отдельных шагов в интеграционном тесте. По умолчанию, кстати, тесты запускаются в алфавитном порядке https://www.baeldung.com/junit-5-test-order

В общем JUnit 5 догнал и перегнал TestNG, рекомендую к использованию если еще не пробовали.

#junit #unittests
Всем привет!

Пару мыслей о BDD фреймворках, самым известным представителем которых является Cucumber.
Пример теста на Cucumber, чтобы было понятно о чем я:

Scenario: Eric wants to withdraw money from his bank account at an ATM
Given Eric has a valid Credit or Debit card
And his account balance is $100
When he inserts his card
And withdraws $45
Then the ATM returns $45
And his account balance is $55

Т.е по сути мы привязываем к методам в коде фразы естественного языка и т.об. можем написать тест как осмысленное предложение.

Мне не понятно, где это может быть полезно.

Основная трудность в BDD - требуется большая подготовительная работа по созданию "словаря", из которого будут конструироваться тесты. Причем если приложение сложное и развивается - работа становится постоянной. Вопрос - а нужно ли делать эту работу?

Рассмотрим возможные случаи использования.

1) разработчики для написания "системных" тестов, проверяющих сервис целиком? Не нужно, разработчику проще написать на обычном JUnit 5, максимум добавить туда Rest Assured как удобным способ в fluent стиле написать тест c четко выделенными стадиями Arrage-Act-Assert "в одну строчку". Оффтоп: почему я слово "в одну строчку" взял в кавычки думаю понятно, а вот насчет "системных" - с терминологией в классификации тестов все плохо, причем когда я начал эту тему изучать лет 6 назад - все было также))) Решение я вижу - определить терминологию в своей команде и ее придерживаться.

2) автотестеры, интеграционное тестирование, тестирование на заглушках, регресс? Как по мне если тестировщик решил перейти в касту автотестеров, то разбираться в Java или Kotlin он должен. Ну или в любом другом языке, где есть хороший тестовый фреймворк. И в DevOps немножко. Очень сложно сделать такую платформу для автотестов, где этих знаний не понадобится.

3) ручные тестировщики? Если тестировщик не хочет в автоматизацию, то я не верю, что BDD может чем-то помочь. Если не прав - хотелось бы узнать про такие кейсы. Снова оффтоп: тестировщик, который не хочет в автоматизацию, не равно плохой тестировщик. Есть роль тест-менеджера, определяющего полноту набора тест-кейсов, состав регресса, пишущего сценарии, и т.об. гарантирующий соответствие кода требованиям. Есть исследовательское тестирование, где автоматизация играет вспомогательную роль.

4) нагрузочное тестирование? Точно не про то: использовать или нет BDD - далеко не та проблема, которая важна для НТ. Профит не понятен

5) системные аналитики? Те, которых я видел "в живой природе", тесты не пишут. У них и так работы хватает: API, схема БД, сиквенс диаграммы, карта экранов, согласования, впитывание и трансформация мыслей продактов...

6) приемочные испытания. Тут развилка. Если мы говорим о приемке внутри одной организации - по моему опыту заинтересованные лица валидируют аналитику заранее, и приемка сводится к демострации и ответах на каверзные вопросы. Возможен прогон тех же автотестов, что и на стадии интеграционного тестирования. И наконец единственный кейс, где BDD может рулить - приемка на стороне организации-заказчика. Для них поставляемое ПО - черный ящик. И то, если процесс не по Agile, и заказчик в процессах команды разработки не участвует.

И еще небольшой гвоздик напоследок - сделать словарь для BDD легко, когда в нем 5-10-20 фраз. Т.е для небольшого приложения. А когда их станет не хватать? Тогда или нужная тяжелая работа по вычистке, универсализации, или словарь станет примитивным - оправь http запрос, прочитай заголовок ответа, а в этом случае BDD становится бесполезным.

#unittests #bdd #testing
Всем привет!

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

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

#unittests #rare_test_libs