Всем привет!
Хочу вернуться к теме "правильных" модульных тестов и подчеркнуть пару важных моментов.
1) должен быть быстрый набор тестов, который можно запускать после каждого изменения в коде. Почему это важно: после небольшого изменения в коде всегда понятна причина падения. После нескольких изменений подряд - уже сложнее. Быстрый - понятие нечеткое, но пойдем от обратного - 10 минут точно не быстро) 5 - тоже многовато IMHO. Идеально - минута, две.
2) как я уже писал - тесты должны быть антихрупкими. Хрупкие тесты с течением времени приводят к такому же результату, как и их отсутствие. Тесты часто падают, их отключают или не запускают, рефакторинг делать страшно, код объявляется legacy и переписывается.
Как этого можно добиться:
- не писать тесты на код, который постоянно меняется. Это один из возможных вариантов, не панацея!) Если это не бизнес-логика - это допустимо. В этом случае модульные тесты можно заменить на интеграционные, проверящие более высокоуровневый результат, которые реже меняется.
- не проверять в тесте детали реализации, тестировать результат, который потребитель тестируемого метода ожидает от него. Хорошая статья на эту тему - https://habr.com/ru/company/jugru/blog/571126/ Тестируя только результат мы теряем точность теста, но увеличиваем антихрупкость. Это необходимый компромис. Исключение: сложные утилитные методы, где алгоритм - порядок вызовов - важен.
3) покрытие кода тестами - не панацея. С одной стороны покрытие 30-50% - плохо, т.к. показывает, что много кода не покрыто тестами. С другой стороны покрытие 100% не говорит, что код хороший. Почему:
- не добавляяя Assert, добавив E2E тесты и закрыв их в try catch можно достичь очень хороших результатов по покрытию)
- важно различать Line Coverage и Condition (Branch) coverage. Первое считает процент покрытых срок, второе - процент протестированных путей, по которым можно прийти от начала к концу метода. В случае SonarQube тут все хорошо - он считает свою метрику, которая совмещает обе https://docs.sonarqube.org/latest/user-guide/metric-definitions/ В теории если покрыты все условия, то и по строчкам кода должно быть 100%. Или в проекте есть код, который не используется. В общем метрике SonarQube можно верить)
- предположим мы написали на первый взгляд полноценный набор тестов, с Assert-ми, все как положено. Покрытие 100% по новому коду. Но есть метод с побочным эффектом - он не только возвращает результат, но и сохраняет его внутри класса для отчетности или сравнения в будущих вызовах. Если этого не знать \ забыть - получим неполноценный тест. Конечно, есть Single Responsibility, неожиданные побочные эффекты это плохо, при TDD забыть про только что написанный код сложно, но в других случаях ситация может быть на практике. Другой кейс - тестируемый метод может вызывать библиотечную функцию, внутренности который мы не знаем. Соответственно, все возможные комбинации параметров для нее тоже не знаем. Не факт, что все такие комбинации нужно тестировать в конретном тесте - мы же не тестируем внешние библиотеки. Но факт, что какие-то важные кейсы для нашего бизнес-процесса можно упустить.
4) принцип Single Responsibility для теста звучит так: тест отвечает за одну бизнес-операцию, единицу поведения. Соотношение 1 тестовый класс = 1 тестируемый объект - не правило. Соответственно, в названии тестового класса не обязательно привязываться к названию тестируемого класса, тем более, что его в будущем могут отрефакторить.
5) ну и финальный момент - серебрянной пули нет, перед написанием тестов надо думать, что и как тестировать и выбирать наилучщий вариант.
P.S. Также хочу посоветовать хорошую книгу про тесты от автора статьи из 2) - https://habr.com/ru/company/piter/blog/528872/
#unittests #books
Хочу вернуться к теме "правильных" модульных тестов и подчеркнуть пару важных моментов.
1) должен быть быстрый набор тестов, который можно запускать после каждого изменения в коде. Почему это важно: после небольшого изменения в коде всегда понятна причина падения. После нескольких изменений подряд - уже сложнее. Быстрый - понятие нечеткое, но пойдем от обратного - 10 минут точно не быстро) 5 - тоже многовато IMHO. Идеально - минута, две.
2) как я уже писал - тесты должны быть антихрупкими. Хрупкие тесты с течением времени приводят к такому же результату, как и их отсутствие. Тесты часто падают, их отключают или не запускают, рефакторинг делать страшно, код объявляется legacy и переписывается.
Как этого можно добиться:
- не писать тесты на код, который постоянно меняется. Это один из возможных вариантов, не панацея!) Если это не бизнес-логика - это допустимо. В этом случае модульные тесты можно заменить на интеграционные, проверящие более высокоуровневый результат, которые реже меняется.
- не проверять в тесте детали реализации, тестировать результат, который потребитель тестируемого метода ожидает от него. Хорошая статья на эту тему - https://habr.com/ru/company/jugru/blog/571126/ Тестируя только результат мы теряем точность теста, но увеличиваем антихрупкость. Это необходимый компромис. Исключение: сложные утилитные методы, где алгоритм - порядок вызовов - важен.
3) покрытие кода тестами - не панацея. С одной стороны покрытие 30-50% - плохо, т.к. показывает, что много кода не покрыто тестами. С другой стороны покрытие 100% не говорит, что код хороший. Почему:
- не добавляяя Assert, добавив E2E тесты и закрыв их в try catch можно достичь очень хороших результатов по покрытию)
- важно различать Line Coverage и Condition (Branch) coverage. Первое считает процент покрытых срок, второе - процент протестированных путей, по которым можно прийти от начала к концу метода. В случае SonarQube тут все хорошо - он считает свою метрику, которая совмещает обе https://docs.sonarqube.org/latest/user-guide/metric-definitions/ В теории если покрыты все условия, то и по строчкам кода должно быть 100%. Или в проекте есть код, который не используется. В общем метрике SonarQube можно верить)
- предположим мы написали на первый взгляд полноценный набор тестов, с Assert-ми, все как положено. Покрытие 100% по новому коду. Но есть метод с побочным эффектом - он не только возвращает результат, но и сохраняет его внутри класса для отчетности или сравнения в будущих вызовах. Если этого не знать \ забыть - получим неполноценный тест. Конечно, есть Single Responsibility, неожиданные побочные эффекты это плохо, при TDD забыть про только что написанный код сложно, но в других случаях ситация может быть на практике. Другой кейс - тестируемый метод может вызывать библиотечную функцию, внутренности который мы не знаем. Соответственно, все возможные комбинации параметров для нее тоже не знаем. Не факт, что все такие комбинации нужно тестировать в конретном тесте - мы же не тестируем внешние библиотеки. Но факт, что какие-то важные кейсы для нашего бизнес-процесса можно упустить.
4) принцип Single Responsibility для теста звучит так: тест отвечает за одну бизнес-операцию, единицу поведения. Соотношение 1 тестовый класс = 1 тестируемый объект - не правило. Соответственно, в названии тестового класса не обязательно привязываться к названию тестируемого класса, тем более, что его в будущем могут отрефакторить.
5) ну и финальный момент - серебрянной пули нет, перед написанием тестов надо думать, что и как тестировать и выбирать наилучщий вариант.
P.S. Также хочу посоветовать хорошую книгу про тесты от автора статьи из 2) - https://habr.com/ru/company/piter/blog/528872/
#unittests #books
Хабр
Школы юнит-тестирования
Существуют две основные школы юнит-тестирования: классическая (ее также называют школой Детройта, или Чикаго) и лондонская (ее также называют мокистской школой, от слова mock). Эти школы кардинально...
Всем привет! Ещё одно ревью на книгу, которую упоминал: Владимир Хориков Принципы юнит-тестирования. https://habr.com/ru/company/piter/blog/528872/ Можно сказать уникальная книга про то, как писать юнит тесты. Я знаю таких всего две - эту и Шаблоны тестирования XUnit. Must read. Некоторые моменты можно обсуждать, но откровенных косяков или воды не нашёл. Что запомнилось.
1) разделение кода на четыре группы: бизнес-логика, контролёр в широком смысле этого слова как код, обрабатывающий входящие запросы, тривиальный код и переусложненный код, в котором есть и внешние зависимости и бизнес-логика. От последнего нужно избавляться через рефакторинг, сводя его к бизнес-логике или контроллерам. На бизнес-логику пишем модульные тесты, на контроллеры - интеграционные. На тривиальный код ничего не пишем)
2) не нужно писать отдельные тесты на каждой класс бизнес-логики, нужно тестировать процессы и стремиться к максимальному покрытию. Для бизнес-логики
3) использование минимум mock в модульных тестах, в идеальном случае их вообще не должно быть. Т.е идея в том, что классы бизнес-логики получают и возвращают DTO, которые можно создать в тесте без всяких mock
4) в случае интеграционных тестов mock нужно делать на неконтролируемые нами внешние зависимости, типа очередей, внешних АС, email транспорта. БД в случае если это не наша частная БД. Если же у нас микросервис со своей БД, которая обновляется вместе с приложением и внешние клиенты в нее не ходят - ее mock-ать не нужно, более того и заменять ее HBase тоже не нужно, т.к иначе мы не сможем полноценно оттестировать работу с БД, да и возникнут накладные расходы на поддержание двух версий скрипов. Это как раз то, где можно дискутировать, но милая мне нравится.
5) чем меньше в приложении слоёв, тем лучше, проще тестировать
6) логирование может являться контрактом, например по требованию сопровождения или если на его основе работает мониторинг, тогда его тоже нужно mock-ать и тестировать.
7) организовать отправку событий, например, логирования, сообщений в Kafka или SMTP можно через стандартизацию механизма событий в доменной модели и обработку событий в слое «контроллеров».
8) если вернутся к модели тестов AAA - Arrange, Act, Assert, то Arrange и Assert в одном тесте может быть много, а вот Act должен быть один, иначе у нас проблемы с нашим Java API. Исключение - интеграционные тесты где сложно поднять контекст.
9) методы с инициализацией тестов типа @Before плохи тем, что сложно читать текст, т.к часть Assert в другом методе. Поэтому лучше использовать отдельные говорящие private/protected методы с параметрами, чтобы можно было настроить тестовые данные под каждый тест
10) название тестового метода должно читаться как фраза, разделять можно подчёркиваниями в случае Java, фраза не обязательно должна быть шаблонной как в BDD, краткость более важна. Кстати, в Kotlin слова в названии теста можно разделять пробелами.
11) интеграционные тесты дают хорошее покрытие, за счёт этого увеличивают защиту от ошибок в коде. Поэтому их лучше всего писать на позитивные сценарии, покрывающие максимум кода и те негативные сценарии, которые не удалось покрыть юнит тестами
12) антихрупкость тестов, о которой я уже писал, становится особенно важна по мере развития проекта. Т.е после некоторого времени более важно, чтобы тесты были антихрупкими, чем чтобы они находили все потенциальные ошибки. Т.к для последнего есть QA и приёмочные тесты
Я перечислил лишь то, что запомнилось, интересным идеей в книге намного больше. Интересных и практических. Повторюсь - must read!
#books #unittests
1) разделение кода на четыре группы: бизнес-логика, контролёр в широком смысле этого слова как код, обрабатывающий входящие запросы, тривиальный код и переусложненный код, в котором есть и внешние зависимости и бизнес-логика. От последнего нужно избавляться через рефакторинг, сводя его к бизнес-логике или контроллерам. На бизнес-логику пишем модульные тесты, на контроллеры - интеграционные. На тривиальный код ничего не пишем)
2) не нужно писать отдельные тесты на каждой класс бизнес-логики, нужно тестировать процессы и стремиться к максимальному покрытию. Для бизнес-логики
3) использование минимум mock в модульных тестах, в идеальном случае их вообще не должно быть. Т.е идея в том, что классы бизнес-логики получают и возвращают DTO, которые можно создать в тесте без всяких mock
4) в случае интеграционных тестов mock нужно делать на неконтролируемые нами внешние зависимости, типа очередей, внешних АС, email транспорта. БД в случае если это не наша частная БД. Если же у нас микросервис со своей БД, которая обновляется вместе с приложением и внешние клиенты в нее не ходят - ее mock-ать не нужно, более того и заменять ее HBase тоже не нужно, т.к иначе мы не сможем полноценно оттестировать работу с БД, да и возникнут накладные расходы на поддержание двух версий скрипов. Это как раз то, где можно дискутировать, но милая мне нравится.
5) чем меньше в приложении слоёв, тем лучше, проще тестировать
6) логирование может являться контрактом, например по требованию сопровождения или если на его основе работает мониторинг, тогда его тоже нужно mock-ать и тестировать.
7) организовать отправку событий, например, логирования, сообщений в Kafka или SMTP можно через стандартизацию механизма событий в доменной модели и обработку событий в слое «контроллеров».
8) если вернутся к модели тестов AAA - Arrange, Act, Assert, то Arrange и Assert в одном тесте может быть много, а вот Act должен быть один, иначе у нас проблемы с нашим Java API. Исключение - интеграционные тесты где сложно поднять контекст.
9) методы с инициализацией тестов типа @Before плохи тем, что сложно читать текст, т.к часть Assert в другом методе. Поэтому лучше использовать отдельные говорящие private/protected методы с параметрами, чтобы можно было настроить тестовые данные под каждый тест
10) название тестового метода должно читаться как фраза, разделять можно подчёркиваниями в случае Java, фраза не обязательно должна быть шаблонной как в BDD, краткость более важна. Кстати, в Kotlin слова в названии теста можно разделять пробелами.
11) интеграционные тесты дают хорошее покрытие, за счёт этого увеличивают защиту от ошибок в коде. Поэтому их лучше всего писать на позитивные сценарии, покрывающие максимум кода и те негативные сценарии, которые не удалось покрыть юнит тестами
12) антихрупкость тестов, о которой я уже писал, становится особенно важна по мере развития проекта. Т.е после некоторого времени более важно, чтобы тесты были антихрупкими, чем чтобы они находили все потенциальные ошибки. Т.к для последнего есть QA и приёмочные тесты
Я перечислил лишь то, что запомнилось, интересным идеей в книге намного больше. Интересных и практических. Повторюсь - must read!
#books #unittests
Хабр
Книга «Принципы юнит-тестирования»
Привет, Хаброжители! Юнит-тестирование — это процесс проверки отдельных модулей программы на корректность работы. Правильный подход к тестированию позволит макс...
Всем привет!
Не 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
Не 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
DZone
Hamcrest vs. AssertJ
Let's compare two popular open Java assertion frameworks, Hamcrest and AssertJ, for testing RESTful APIs. Check out their features, syntax, and other perks.
Всем привет!
Давно хотел написать про паттерны/шаблоны программирования. Основной вопрос, возникающий при разговоре про паттерны - какая от них польза? Ведь главное - умеет человек кодить или нет.
С одной стороны паттерны - это лишь часть арсенала программиста. Можно заучить все паттерны, но не научиться кодить.
И тут возникает второй вопрос - о каких паттернах мы говорим?
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
Давно хотел написать про паттерны/шаблоны программирования. Основной вопрос, возникающий при разговоре про паттерны - какая от них польза? Ведь главное - умеет человек кодить или нет.
С одной стороны паттерны - это лишь часть арсенала программиста. Можно заучить все паттерны, но не научиться кодить.
И тут возникает второй вопрос - о каких паттернах мы говорим?
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
refactoring.guru
Каталог паттернов проектирования
Список паттернов проектирования, сгруппированый по предназанчению, сложности и популярности паттернов. В каталог включены объектно ориентированные паттерны, а также некоторые архитектурные паттерны.
Всем привет!
По следам отличной книги Владимира Хорикова Принципы юнит-тестирования, см 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
По следам отличной книги Владимира Хорикова Принципы юнит-тестирования, см 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
Telegram
(java || kotlin) && devOps
Всем привет! Ещё одно ревью на книгу, которую упоминал: Владимир Хориков Принципы юнит-тестирования. https://habr.com/ru/company/piter/blog/528872/ Можно сказать уникальная книга про то, как писать юнит тесты. Я знаю таких всего две - эту и Шаблоны тестирования…
Всем привет!
Надеюсь все уже перешли на 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
Надеюсь все уже перешли на 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
Baeldung
Guide to Dynamic Tests in Junit 5 | Baeldung
Learn about dynamic tests introduced in JUnit 5 - a new programming model that supports full test lifecycle.