Всем привет!
Помимо знания языка программирования и инструментов, о которых я писал в предыдущих постах, есть еще один важный навык, который часто недооценивают. Я про знание среды разработки, она же IDE. Это инструмент, с которым мы работаем каждый день. Позволяет автоматизировать большое количество рутинных действий и съэкономить кучу времени.
Предположу, что у большинства Java разработчиков IDE - это IntelliJ IDEA. Бесплатная Community или платная Ultimate.
Да, конечно, существуют Netbeans и Eclipse. У первой сложная судьба - начинала как "придворная" IDE в Sun\Oracle, а заканчивает как OpenSource в Apache Foundation.
Вторая - изначально OpenSource, с акцентом на расширяемость плагинами, довольно активно развивается. Еще Eclipse - живой пример использования OSGi. Но есть нюанс. На мой взгляд любой успешный (!) коммерческий продукт, создатели которого не продали душу богу маркетинга (!) - т.е. не соблазнились свистелками, а вместо этого думают о пользователе - будет бить OpenSource за счет возможности координации усилий. Приведу примеры. Вышла новая версия Java - оперативно добавили поддержку. Популярен сейчас Kotlin или новый фреймворк - наняли людей, перебросили команду и добавили поддержку. Поняли, что можно упростить жизнь разработчику готовыми инструментами для рефакторинга - составили список и автоматизировали. Скоордировать сообщество разработчиков, для которых OpenSource - хобби, для быстрого решения подобных задач сложнее. Хотя наверное при определенном количестве commiters - возможно.
Но я отвлекся. Сегодня речь про IDEA. IDEA может многое. Пречисленные ниже фичи в разное время меня впечатлили, и я считаю на них стоит обратить внимание:
- автодополнение: по идущим не подряд символам из названия метода набранным в нужном порядке, по символам в неверной раскладке, с сортировкой исходя из машинного обучения. Да, да ИИ уже с нами)
https://www.jetbrains.com/help/idea/auto-completing-code.html
- автогенерация: тесты к классу, override, конструкторы, getter и setter https://www.jetbrains.com/help/idea/generating-code.html
- огромное число рефакторингов, причем достаточно умных. Пример умного рефакторинга: при переименовании IDEA отдельно спрашивает подтверждение на совпадения в комментариях. https://www.jetbrains.com/help/idea/refactoring-source-code.html
- всеобъемлющий поиск: по настройкам, по классам, файлам, действиям в IDE, и по всему сразу, поиск использования класса\метода, по коду проекта и поиск внутри файла, в т.ч с регуляркой, структурный поиск - найти все enum в проекте, поиск по методам класса.
Даже перечисление видов поиска заняло кучу места, а мой список не полон) А еще есть вкладки с предыдущими результатами поиска, самобновляющиеся после редактирования кода.
https://www.jetbrains.com/help/idea/finding-and-replacing-text-in-file.html
- отличная интеграция с Git. И скорее всего с другими VCS, но я тестировал только Git. Фичи: удобно выбирать файлы и даже строки для коммита, на коммит можно повесить проверки, простые Revert, Merge, Amend commit, сравнения файлов. В целом для 99% процентов задач можно забыть про сторонние клиенты Git.
https://www.jetbrains.com/help/idea/version-control-integration.html
- рендеринг кода в редакторе: отображение "лямбд" еще до их появления в Java 7))), упрощенное представление лямбд, компактный JavaDoc, работа с секциями
- подсказки по коду: краткая и полная информация о классе или методе, всплывающий JavaDoc, подкачивание исходников при их наличии. https://www.jetbrains.com/help/idea/viewing-reference-information.html
- анализ зависимостей между классами, в т.ч в виде матрицы. Позволяет распутывать клубки в больших проектах. https://www.jetbrains.com/help/idea/dependencies-analysis.html
- возможность скачивания JDK разных версий и вендоров прямо из IDE
- HTTP client
- Code with Me - возможность совместной работы, хотя я ее так и не оттестировал(
- анализ стектрейсов, полученных от тестировщиков или сопровождения, переход к коду из стэктрейса https://www.jetbrains.com/help/idea/analyzing-external-stacktraces.html
Помимо знания языка программирования и инструментов, о которых я писал в предыдущих постах, есть еще один важный навык, который часто недооценивают. Я про знание среды разработки, она же IDE. Это инструмент, с которым мы работаем каждый день. Позволяет автоматизировать большое количество рутинных действий и съэкономить кучу времени.
Предположу, что у большинства Java разработчиков IDE - это IntelliJ IDEA. Бесплатная Community или платная Ultimate.
Да, конечно, существуют Netbeans и Eclipse. У первой сложная судьба - начинала как "придворная" IDE в Sun\Oracle, а заканчивает как OpenSource в Apache Foundation.
Вторая - изначально OpenSource, с акцентом на расширяемость плагинами, довольно активно развивается. Еще Eclipse - живой пример использования OSGi. Но есть нюанс. На мой взгляд любой успешный (!) коммерческий продукт, создатели которого не продали душу богу маркетинга (!) - т.е. не соблазнились свистелками, а вместо этого думают о пользователе - будет бить OpenSource за счет возможности координации усилий. Приведу примеры. Вышла новая версия Java - оперативно добавили поддержку. Популярен сейчас Kotlin или новый фреймворк - наняли людей, перебросили команду и добавили поддержку. Поняли, что можно упростить жизнь разработчику готовыми инструментами для рефакторинга - составили список и автоматизировали. Скоордировать сообщество разработчиков, для которых OpenSource - хобби, для быстрого решения подобных задач сложнее. Хотя наверное при определенном количестве commiters - возможно.
Но я отвлекся. Сегодня речь про IDEA. IDEA может многое. Пречисленные ниже фичи в разное время меня впечатлили, и я считаю на них стоит обратить внимание:
- автодополнение: по идущим не подряд символам из названия метода набранным в нужном порядке, по символам в неверной раскладке, с сортировкой исходя из машинного обучения. Да, да ИИ уже с нами)
https://www.jetbrains.com/help/idea/auto-completing-code.html
- автогенерация: тесты к классу, override, конструкторы, getter и setter https://www.jetbrains.com/help/idea/generating-code.html
- огромное число рефакторингов, причем достаточно умных. Пример умного рефакторинга: при переименовании IDEA отдельно спрашивает подтверждение на совпадения в комментариях. https://www.jetbrains.com/help/idea/refactoring-source-code.html
- всеобъемлющий поиск: по настройкам, по классам, файлам, действиям в IDE, и по всему сразу, поиск использования класса\метода, по коду проекта и поиск внутри файла, в т.ч с регуляркой, структурный поиск - найти все enum в проекте, поиск по методам класса.
Даже перечисление видов поиска заняло кучу места, а мой список не полон) А еще есть вкладки с предыдущими результатами поиска, самобновляющиеся после редактирования кода.
https://www.jetbrains.com/help/idea/finding-and-replacing-text-in-file.html
- отличная интеграция с Git. И скорее всего с другими VCS, но я тестировал только Git. Фичи: удобно выбирать файлы и даже строки для коммита, на коммит можно повесить проверки, простые Revert, Merge, Amend commit, сравнения файлов. В целом для 99% процентов задач можно забыть про сторонние клиенты Git.
https://www.jetbrains.com/help/idea/version-control-integration.html
- рендеринг кода в редакторе: отображение "лямбд" еще до их появления в Java 7))), упрощенное представление лямбд, компактный JavaDoc, работа с секциями
- подсказки по коду: краткая и полная информация о классе или методе, всплывающий JavaDoc, подкачивание исходников при их наличии. https://www.jetbrains.com/help/idea/viewing-reference-information.html
- анализ зависимостей между классами, в т.ч в виде матрицы. Позволяет распутывать клубки в больших проектах. https://www.jetbrains.com/help/idea/dependencies-analysis.html
- возможность скачивания JDK разных версий и вендоров прямо из IDE
- HTTP client
- Code with Me - возможность совместной работы, хотя я ее так и не оттестировал(
- анализ стектрейсов, полученных от тестировщиков или сопровождения, переход к коду из стэктрейса https://www.jetbrains.com/help/idea/analyzing-external-stacktraces.html
IntelliJ IDEA Help
Code completion | IntelliJ IDEA
Techniques to speed up the editing process in IntelliJ IDEA using code completion (basic completion, smart
completion based on a type, hippie expand, and so on).
completion based on a type, hippie expand, and so on).
- быстрое получение ссылки на код: сopy path или сopy reference, а с внешними плагинами и Copy Bitbucket Link
- shortcuts практически для всего: https://www.jetbrains.com/help/idea/reference-keymap-win-default.html А для действий без стандартных shortcuts можно назначить свои, причем не только клавиатурные, но и мышинные)
Рекомендую начинать учить, если еще не сделали.
Начать можно с https://blog.jetbrains.com/idea/2020/03/top-15-intellij-idea-shortcuts/
Также не стоит выключать обучающий режим, когда при открытии проекта IDEA показывает полезные советы, в т.ч. shortcuts.
- разнообразие внешних плагинов, рекомендую заглянуть в соответствующий раздел настроек.
В общем IDEA - это целый мир, в который можно и нужно погрузится с головой.
Возможно я что-то упустил из крутых или важных фичей, напишите об этом.
#IDEA #IDE #tools #обучение
- shortcuts практически для всего: https://www.jetbrains.com/help/idea/reference-keymap-win-default.html А для действий без стандартных shortcuts можно назначить свои, причем не только клавиатурные, но и мышинные)
Рекомендую начинать учить, если еще не сделали.
Начать можно с https://blog.jetbrains.com/idea/2020/03/top-15-intellij-idea-shortcuts/
Также не стоит выключать обучающий режим, когда при открытии проекта IDEA показывает полезные советы, в т.ч. shortcuts.
- разнообразие внешних плагинов, рекомендую заглянуть в соответствующий раздел настроек.
В общем IDEA - это целый мир, в который можно и нужно погрузится с головой.
Возможно я что-то упустил из крутых или важных фичей, напишите об этом.
#IDEA #IDE #tools #обучение
IntelliJ IDEA Help
Predefined Windows keymap | IntelliJ IDEA
Всем привет!
Наверняка многие слышали про практику TDD - Test Driven Development. У которой есть некие сложности с применением. Напомню, ее суть не только и не столько в том, что тест должен предшествовать коду, а в сокращении врени итерации - тест-код. Т.е. в идеальном случае надо делать так:
1) простейший тест
2) код с заглушкой, позволяющий пройти тест
3) новый тест
4) усложнение заглушки
...
N) финальный работающий код
И коммитить желательно почаще.
Самое сложное здесь - заставить себя не писать сразу кучу тестов или весь метод сразу. Или даже весь класс + ряд зависимых классов, а идти мелкими шагами.
Сам пробовал - сложно)
И от одного из создателей XP Кента Бека есть решение - TCR https://medium.com/@kentbeck_7670/test-commit-revert-870bbd756864
Расшифровывается как test && commit | | revert.
Суть в следующем:
1) к запуску тестов привязывается команда git
2) если тесты успешные - git commit
3) если тесты упали, то коммитить неработающий код нельзя, потому делаем revert в виде git reset --hard
Только хардкор!)
4) в паралльном потоке работает цикл
while (true) {
git pull --rebase
git push
}
синхронизируя изменения других разработчиков.
Итог: переписав несколько раз большие куски кода из-за падения теста рано или поздно длина итераций станет минимально возможной.
Второй возможный кейс - кусок кода небольшой, но commit вовремя не сделан, и кто-то что-то параллельно закоммитил в develop и поломал ваши тесты.
Пример использования на практике с реализацией shell скрипта "test && commit | | revert":
https://medium.com/@tdeniffel/tcr-test-commit-revert-a-test-alternative-to-tdd-6e6b03c22bec
Что думаете?
#Agile #XP #TDD #TCR
Наверняка многие слышали про практику TDD - Test Driven Development. У которой есть некие сложности с применением. Напомню, ее суть не только и не столько в том, что тест должен предшествовать коду, а в сокращении врени итерации - тест-код. Т.е. в идеальном случае надо делать так:
1) простейший тест
2) код с заглушкой, позволяющий пройти тест
3) новый тест
4) усложнение заглушки
...
N) финальный работающий код
И коммитить желательно почаще.
Самое сложное здесь - заставить себя не писать сразу кучу тестов или весь метод сразу. Или даже весь класс + ряд зависимых классов, а идти мелкими шагами.
Сам пробовал - сложно)
И от одного из создателей XP Кента Бека есть решение - TCR https://medium.com/@kentbeck_7670/test-commit-revert-870bbd756864
Расшифровывается как test && commit | | revert.
Суть в следующем:
1) к запуску тестов привязывается команда git
2) если тесты успешные - git commit
3) если тесты упали, то коммитить неработающий код нельзя, потому делаем revert в виде git reset --hard
Только хардкор!)
4) в паралльном потоке работает цикл
while (true) {
git pull --rebase
git push
}
синхронизируя изменения других разработчиков.
Итог: переписав несколько раз большие куски кода из-за падения теста рано или поздно длина итераций станет минимально возможной.
Второй возможный кейс - кусок кода небольшой, но commit вовремя не сделан, и кто-то что-то параллельно закоммитил в develop и поломал ваши тесты.
Пример использования на практике с реализацией shell скрипта "test && commit | | revert":
https://medium.com/@tdeniffel/tcr-test-commit-revert-a-test-alternative-to-tdd-6e6b03c22bec
Что думаете?
#Agile #XP #TDD #TCR
Medium
test && commit || revert
As part of Limbo on the Cheap, we invented a new programming workflow. I introduced “test && commit”, where every time the tests run…
Всем привет!
Чтобы закончить тему с TDD, ну хотя бы на ближайшее время закончить, хочу подчеркнуть его плюсы и порекомендовать книгу.
Плюсы:
1) точно достигните требуемого покрытия кода тестами. Мне сложно представить откуда может взяться непокрытый тестами код если приложение создано по TDD начиная с первой строчки
2) писать тесты будет не так тяжело, как при типичном подходе. Когда код написан, отлажен, возможно даже прошел функциональное тестирование, а вместо создания чего-то нового или обучения приходится удовлетворять SonarQube и требования организации\тимлида - это не весело))) А тут еще выясняется, что код плохо приспособлен для тестирования. И кто знает, возможно с TDD даже появятся позитивные эмоциии, особенно когда тесты зеленеют)
3) меньше шансов, что приложение, даже если оно изначально разрабатывалось как микросервис, превратится в монолит(ик), который страшно трогать руками. Железо не гибкое, процессы и люди .. ну под вопросом. А ПО должно быть гибким.
4) не нужно объяснять PO что такое рефакторинг и почему для него выделяется столько много storypoins или человекодней. Т.к. рефакторинг станет неотрывной частью процесса разработки и затраты на него войдут в трудоемкость разработки фичи
5) полученные тесты - хороший аргумент, чтобы оставить в JavaDoc и аналитике только вещи, касающиеся описания бизнес-процесса, e2e, интеграций, т.к. все остальное будет в тестах. Причем документация с помощью тестов может разойтись с кодом только в одном случае - если на тесты забить) В отличие от.
6) возможно реализация логики улучшится. Как это может произойти. Часто в коде реализуется первая пришедшая в голову идея. А уже по результатам функционального тестирования она переписывается или что хуже запиливаюстя костыли. Это плохой подход - брать первую попавшуюся идею, но психологически бороться с ним сложно. Если же эволюционно писать тесты-код-тесты-код.. то быстрее можно прийти к пониманию, что первая реализация - не лучшая. А переписывание кода и тестов в TDD - это ок
Ну и книга. Это не Кент Бек "Экстремальное программирование: разработка через тестирование" https://habr.com/ru/company/piter/blog/326662/ как можно было подумать. Хотя и ее тоже можно порекомендовать, классика как никак.
Но недавно вышла новая книга Боба Мартина, он же «дядюшка» Боб "Идеальная работа. Программирование без прикрас" https://habr.com/ru/company/piter/blog/679378/. Первая часть книги как раз рассказывает как влится в TDD. Даже видосы есть с реализацией разных простых алгоритмов по TDD подходу.
Мне очень зашло, рекомендую, книгу вместе с видосами. Да, если вступление к книге покажется слишком официозным - переходите сразу к первой части.
Если книжки, статьи на разные темы в рамках Java\Kotlin\DevOps интересны - пишите, буду рекомендовать.
#books #tdd
Чтобы закончить тему с TDD, ну хотя бы на ближайшее время закончить, хочу подчеркнуть его плюсы и порекомендовать книгу.
Плюсы:
1) точно достигните требуемого покрытия кода тестами. Мне сложно представить откуда может взяться непокрытый тестами код если приложение создано по TDD начиная с первой строчки
2) писать тесты будет не так тяжело, как при типичном подходе. Когда код написан, отлажен, возможно даже прошел функциональное тестирование, а вместо создания чего-то нового или обучения приходится удовлетворять SonarQube и требования организации\тимлида - это не весело))) А тут еще выясняется, что код плохо приспособлен для тестирования. И кто знает, возможно с TDD даже появятся позитивные эмоциии, особенно когда тесты зеленеют)
3) меньше шансов, что приложение, даже если оно изначально разрабатывалось как микросервис, превратится в монолит(ик), который страшно трогать руками. Железо не гибкое, процессы и люди .. ну под вопросом. А ПО должно быть гибким.
4) не нужно объяснять PO что такое рефакторинг и почему для него выделяется столько много storypoins или человекодней. Т.к. рефакторинг станет неотрывной частью процесса разработки и затраты на него войдут в трудоемкость разработки фичи
5) полученные тесты - хороший аргумент, чтобы оставить в JavaDoc и аналитике только вещи, касающиеся описания бизнес-процесса, e2e, интеграций, т.к. все остальное будет в тестах. Причем документация с помощью тестов может разойтись с кодом только в одном случае - если на тесты забить) В отличие от.
6) возможно реализация логики улучшится. Как это может произойти. Часто в коде реализуется первая пришедшая в голову идея. А уже по результатам функционального тестирования она переписывается или что хуже запиливаюстя костыли. Это плохой подход - брать первую попавшуюся идею, но психологически бороться с ним сложно. Если же эволюционно писать тесты-код-тесты-код.. то быстрее можно прийти к пониманию, что первая реализация - не лучшая. А переписывание кода и тестов в TDD - это ок
Ну и книга. Это не Кент Бек "Экстремальное программирование: разработка через тестирование" https://habr.com/ru/company/piter/blog/326662/ как можно было подумать. Хотя и ее тоже можно порекомендовать, классика как никак.
Но недавно вышла новая книга Боба Мартина, он же «дядюшка» Боб "Идеальная работа. Программирование без прикрас" https://habr.com/ru/company/piter/blog/679378/. Первая часть книги как раз рассказывает как влится в TDD. Даже видосы есть с реализацией разных простых алгоритмов по TDD подходу.
Мне очень зашло, рекомендую, книгу вместе с видосами. Да, если вступление к книге покажется слишком официозным - переходите сразу к первой части.
Если книжки, статьи на разные темы в рамках Java\Kotlin\DevOps интересны - пишите, буду рекомендовать.
#books #tdd
Хабр
Книга «Экстремальное программирование: разработка через тестирование»
Возвращение знаменитого бестселлера. Изящный, гибкий и понятный код, который легко модифицировать, который корректно работает и который не подкидывает своим создателям неприятных сюрпризов. Неужели...
Всем привет!
Каким должен быть хороший тест?
Я в первую очередь про модульные (unit), но в принципе правила применимы к любым.
Основные моменты:
1) правило Arrange, Act, Assert https://xp123.com/articles/3a-arrange-act-assert/
Тест делится на три части: подготовка тестовых данных, вызов тестового метода и проверка. Часто забывают про последнюю.
Тест должен что-то проверить: выброшенное исключение, сколько и каких было вызвано методов, состояние объекта.
Тест проверяющий только тот факт, что вызов прошел без исключения, добавляет покрытия, но по сути является недотестом. Его успех показывает, что что-то там выполнилось) Выполнено ли то, что должен делать метод - не ясно. Если API не позволяет проверить результат выполнения кода - это плохое API. Если же код legacy и рефакторить его сложно - можно воспользоваться Mockito.spy или рефлексией.
2) тестовый код - такой же код, как и боевой. К нему должны быть применены все практики написания чистого кода - https://www.litres.ru/robert-s-martin/chistyy-kod-sozdanie-analiz-i-refaktoring-6444478
Я вижу одно исключение - в тестовом коде норм использовать System.out.println
3) тест должен проверять одну операцию с одним или несколькими связанными наборами входных параметров, это удобно делать через параметризацию. При этом допустимо в тесте использовать несколько Assert. Хотя можно их вынести в один assert метод. Или использовать Soft Assert http://joel-costigliola.github.io/assertj/assertj-core-features-highlight.html#soft-assertions. Как по мне - все три варианта норм, дело вкуса.
4) все внешние объекты, требуемые тестируемому методу, должны быть заглушены. Наиболее удобно использовать Mockito.mock или Mockito.spy, но если надо - можно наследоваться от интерфейса и сделать тестовый двойник самому. Если глушить приходится слишком много - повод задуматься про архитектуру кода
5) если стандартного API Mockito или самописных заглушек не хватает, и руки тянутся к включить "секретные" опции Mockito для того, чтобы заглушить private, static или наследоваться от final класса - тоже повод задуматься про архитектуру. Хотя как быстрое решение для legacy допустимо.
6) в поставке для ПРОМа не должно быть тестового кода, боевой код не должен использовать тестовые зависимости и Helpers. В боевом коде не должно быть "ловушек" для успешного выполнения тестов - т.е. выражений типа
if (isTestRun()) {
7) тесты - это документация к коду. Для этого тест должен легко читаться. А чтобы этого добиться - нужно выносить все лишние во вспомогательные методы для Arrange и Assert, передавая как параметры в эти методы только то, что непосредственно влияет на конкретный тест. В этом плане при выборе между @ BeforeEach методом и обычным нужно выбирать обычный - так замысел теста легче читается.
А при возникновении вопроса чтобы бы добавить в тесты, или может уже хватит, стоит в первую очередь думать о закрытии всех основных комбинаций входных параметров, а не о покрытии. Требуемый процент покрытия можно подкрутить, главное как и у тестировщиков - полнота тестовой модели. И читаемость тестов
8) тесты должны быть антихрупкими. Т.е. тест не должен падать при любых правках кода, а тем более - при правках кода, который на первый взгляд никак не связан с тестом. Задача теста - облегчить рефакторинг и доработки, а не усложнить их) Готовых рецептов тут нет, но если данные для assert меняются каждый релиз, значить проверять надо что-то еще. Пример: если речь про API - атрибуты JSON, а не полный JSON
9) тесты не должны зависеть от результата других тестов, от порядка выполнения и от среды
10) из логов запуска упавшего теста должно быть понятно, в чем ошибка. Для этого тест должен выполняться быстро, чтобы запускать его после каждого изменения, тестовые методы должны быть названы осознано, и самое сложное - в assert-ах должны быть сообщения об ошибках. И \ или использовать power asserts, которые подробно выводят в лог что пришло в Assert метод https://github.com/bnorm/kotlin-power-assert
P.S. Женя Осмаковский, спасибо за подсказку по п.8
#unittests #cleancode
Каким должен быть хороший тест?
Я в первую очередь про модульные (unit), но в принципе правила применимы к любым.
Основные моменты:
1) правило Arrange, Act, Assert https://xp123.com/articles/3a-arrange-act-assert/
Тест делится на три части: подготовка тестовых данных, вызов тестового метода и проверка. Часто забывают про последнюю.
Тест должен что-то проверить: выброшенное исключение, сколько и каких было вызвано методов, состояние объекта.
Тест проверяющий только тот факт, что вызов прошел без исключения, добавляет покрытия, но по сути является недотестом. Его успех показывает, что что-то там выполнилось) Выполнено ли то, что должен делать метод - не ясно. Если API не позволяет проверить результат выполнения кода - это плохое API. Если же код legacy и рефакторить его сложно - можно воспользоваться Mockito.spy или рефлексией.
2) тестовый код - такой же код, как и боевой. К нему должны быть применены все практики написания чистого кода - https://www.litres.ru/robert-s-martin/chistyy-kod-sozdanie-analiz-i-refaktoring-6444478
Я вижу одно исключение - в тестовом коде норм использовать System.out.println
3) тест должен проверять одну операцию с одним или несколькими связанными наборами входных параметров, это удобно делать через параметризацию. При этом допустимо в тесте использовать несколько Assert. Хотя можно их вынести в один assert метод. Или использовать Soft Assert http://joel-costigliola.github.io/assertj/assertj-core-features-highlight.html#soft-assertions. Как по мне - все три варианта норм, дело вкуса.
4) все внешние объекты, требуемые тестируемому методу, должны быть заглушены. Наиболее удобно использовать Mockito.mock или Mockito.spy, но если надо - можно наследоваться от интерфейса и сделать тестовый двойник самому. Если глушить приходится слишком много - повод задуматься про архитектуру кода
5) если стандартного API Mockito или самописных заглушек не хватает, и руки тянутся к включить "секретные" опции Mockito для того, чтобы заглушить private, static или наследоваться от final класса - тоже повод задуматься про архитектуру. Хотя как быстрое решение для legacy допустимо.
6) в поставке для ПРОМа не должно быть тестового кода, боевой код не должен использовать тестовые зависимости и Helpers. В боевом коде не должно быть "ловушек" для успешного выполнения тестов - т.е. выражений типа
if (isTestRun()) {
7) тесты - это документация к коду. Для этого тест должен легко читаться. А чтобы этого добиться - нужно выносить все лишние во вспомогательные методы для Arrange и Assert, передавая как параметры в эти методы только то, что непосредственно влияет на конкретный тест. В этом плане при выборе между @ BeforeEach методом и обычным нужно выбирать обычный - так замысел теста легче читается.
А при возникновении вопроса чтобы бы добавить в тесты, или может уже хватит, стоит в первую очередь думать о закрытии всех основных комбинаций входных параметров, а не о покрытии. Требуемый процент покрытия можно подкрутить, главное как и у тестировщиков - полнота тестовой модели. И читаемость тестов
8) тесты должны быть антихрупкими. Т.е. тест не должен падать при любых правках кода, а тем более - при правках кода, который на первый взгляд никак не связан с тестом. Задача теста - облегчить рефакторинг и доработки, а не усложнить их) Готовых рецептов тут нет, но если данные для assert меняются каждый релиз, значить проверять надо что-то еще. Пример: если речь про API - атрибуты JSON, а не полный JSON
9) тесты не должны зависеть от результата других тестов, от порядка выполнения и от среды
10) из логов запуска упавшего теста должно быть понятно, в чем ошибка. Для этого тест должен выполняться быстро, чтобы запускать его после каждого изменения, тестовые методы должны быть названы осознано, и самое сложное - в assert-ах должны быть сообщения об ошибках. И \ или использовать power asserts, которые подробно выводят в лог что пришло в Assert метод https://github.com/bnorm/kotlin-power-assert
P.S. Женя Осмаковский, спасибо за подсказку по п.8
#unittests #cleancode
Xp123
3A - Arrange, Act, Assert - XP123
Arrange-Act-Assert is a way to structure microtests - to make sure they clearly do one thing and aren't like a run-on-sentence.
Всем привет!
Пару слов о том, на что не нужно писать модульные тесты.
Женя, спасибо за вопросы к предыдущему посту!
Для начала очевидные кейсы:
1) getter и setter. Во-первый - логики там нет, точнее не должно быть. Более того эти методы скорее всего будут вызываны из других тестов. Более того, методы вообще говоря можно не писать руками, а сгенерировать с помощью Lombook @Getter и @Setter, или использовать Kotlin. Что касается покрытия, то как я уже писал ранее - покрытие не главная цель, скорее следствие. А кроме того Jacoco -как самый распространенный инструмент - не считает покрытие по сгенерированным методам Lombook. Детальнее про то, что не учитывается в покрытии и что можно донастроить см. https://www.baeldung.com/jacoco-report-exclude и https://github.com/jacoco/jacoco/wiki/filtering-JAVAC.SYNTHMETH
2) сгенерированный код. Причины те же, как исключить см. выше.
3) код внешних библиотек. Как первая версия теста при TDD подходе, чтобы проверить, что код вообще компилируется - да, можно. Чтобы убедиться, что JDK правильная в проекте, все библиотеки подключены. Проверить что новая библиотека работает как ожидаешь. Но потом такой тест или должен быть дополнен тестированием вашей логики, или удален. Код поднятия внешних библиотек при этом может быть вынесен в отдельный метод для переиспользования.
4) любой тривиальный код, типа конструкторы Entity\DTO или число элементов в Enum. В первом случае этот код должен быть вызван из других тестов. Во втором - не понятно, что доказывает этот тест.
Неоднозначные случаи:
5) корректноть инфраструктурного кода, типа создания контекста Spring. В идеале ее должен тестировать компилятор. Что он и сделает при использовании JavaConfig + Annotations. Если у вас XML Config - может помочь IDEA https://www.jetbrains.com/help/idea/spring-projects.html, ну или поднятие Spring контекста в других тестах.
6) методы-интеграторы: методы, единственная цель которых - вызывать другие методы. Как правило находятся в сервисах. Во-первых код теста получается хрупким, т.к. с новыми релизами добавляются новые вызовы. А во-вторых приходится писать кучу моков. А главное логики особо нет, кроме правильной последовательности вызовов и передачи данных по цепочке. Если появляется более сложная логика - валидация данных, проверка прав - тест будет иметь смысл. Иначе можно пропустить, а лучше написать интеграционный тест, включающий вызов такого метода-интегратора.
7) методы, конструирующие DTO для слоя API. DTO использовать нужно, чтобы развязать доменные сущности и API. Но такой код часто представляет просто mapping. С одной стороны какая-то практическая польза в такой проверке есть: данные должны быть в нужном формате - JSON, должны быть какие-то обязательные поля, нужно убедится, что данные из модели попали в ответ. С другой стороны если валидировать сравнением с эталонным JSON - тест получается хрупким. Решение - метод тестировать, но проверять результат преобразования точечно, фокусироваться на том, что скорее всего не изменится в будущем.
В заключение вот хорошая статья на тему написания правльных тестов https://habr.com/ru/company/jugru/blog/323920/
#unittests #Java
Пару слов о том, на что не нужно писать модульные тесты.
Женя, спасибо за вопросы к предыдущему посту!
Для начала очевидные кейсы:
1) getter и setter. Во-первый - логики там нет, точнее не должно быть. Более того эти методы скорее всего будут вызываны из других тестов. Более того, методы вообще говоря можно не писать руками, а сгенерировать с помощью Lombook @Getter и @Setter, или использовать Kotlin. Что касается покрытия, то как я уже писал ранее - покрытие не главная цель, скорее следствие. А кроме того Jacoco -как самый распространенный инструмент - не считает покрытие по сгенерированным методам Lombook. Детальнее про то, что не учитывается в покрытии и что можно донастроить см. https://www.baeldung.com/jacoco-report-exclude и https://github.com/jacoco/jacoco/wiki/filtering-JAVAC.SYNTHMETH
2) сгенерированный код. Причины те же, как исключить см. выше.
3) код внешних библиотек. Как первая версия теста при TDD подходе, чтобы проверить, что код вообще компилируется - да, можно. Чтобы убедиться, что JDK правильная в проекте, все библиотеки подключены. Проверить что новая библиотека работает как ожидаешь. Но потом такой тест или должен быть дополнен тестированием вашей логики, или удален. Код поднятия внешних библиотек при этом может быть вынесен в отдельный метод для переиспользования.
4) любой тривиальный код, типа конструкторы Entity\DTO или число элементов в Enum. В первом случае этот код должен быть вызван из других тестов. Во втором - не понятно, что доказывает этот тест.
Неоднозначные случаи:
5) корректноть инфраструктурного кода, типа создания контекста Spring. В идеале ее должен тестировать компилятор. Что он и сделает при использовании JavaConfig + Annotations. Если у вас XML Config - может помочь IDEA https://www.jetbrains.com/help/idea/spring-projects.html, ну или поднятие Spring контекста в других тестах.
6) методы-интеграторы: методы, единственная цель которых - вызывать другие методы. Как правило находятся в сервисах. Во-первых код теста получается хрупким, т.к. с новыми релизами добавляются новые вызовы. А во-вторых приходится писать кучу моков. А главное логики особо нет, кроме правильной последовательности вызовов и передачи данных по цепочке. Если появляется более сложная логика - валидация данных, проверка прав - тест будет иметь смысл. Иначе можно пропустить, а лучше написать интеграционный тест, включающий вызов такого метода-интегратора.
7) методы, конструирующие DTO для слоя API. DTO использовать нужно, чтобы развязать доменные сущности и API. Но такой код часто представляет просто mapping. С одной стороны какая-то практическая польза в такой проверке есть: данные должны быть в нужном формате - JSON, должны быть какие-то обязательные поля, нужно убедится, что данные из модели попали в ответ. С другой стороны если валидировать сравнением с эталонным JSON - тест получается хрупким. Решение - метод тестировать, но проверять результат преобразования точечно, фокусироваться на том, что скорее всего не изменится в будущем.
В заключение вот хорошая статья на тему написания правльных тестов https://habr.com/ru/company/jugru/blog/323920/
#unittests #Java
GitHub
filtering JAVAC.SYNTHMETH
:microscope: Java Code Coverage Library. Contribute to jacoco/jacoco development by creating an account on GitHub.
Всем привет!
Раз уж заговорили про тесты расскажу про отладку и тестирование Jenkins pipeline. Для начала: отладка и тестирование Jenkins pipeline - это боль(((
Почему?
1) pipeline пишется на Groovy, а Groovy - это язык с динамической типизацией. Динамическая типизация хорошо подходит для небольших скриптов, но как только кода становится много - код сыпется, править его становится страшно.
Да, есть аннотация @groovy.transform.CompileStatic, но см. п.2
2) pipeline пишется не просто на Groovy, а на Groovy DSL. Стандартный Jenkins предоставляет ряд команд, они же шаги, плюс их число расширяется плагинами. Список см. https://www.jenkins.io/doc/pipeline/steps/
Т.е классов, описывающих синтаксис pipeline просто нет.
3) чтобы протестировать pipeline вживую нужно время. На ожидание свободного Jenkins slave, на скачивание кода пайплайн. Т.е. цикл обратной связи получается большим.
Что же можно сделать.
1) как только пайплайн перестает быть элементарным или появляется общий код - выносим его в shared library: https://www.jenkins.io/doc/book/pipeline/shared-libraries/
Она может содержать как DSL - в папке vars, так и обычные Groovy классы в папке src. А на обычные классы можно и нужно писать модульные тесты.
Как вариант - пайплайн и библиотека могут быть в одном репозитории, тогда библиотеку из pipeline можно загрузить так:
library identifier: "selflib@${scm.branches[0]}", retriever: legacySCM(scm)
В скрипте pipeline, который указан в джобе, оставляем минимум кода. Почему это полезно кроме упрощения тестирования - код в pipeline по сути является статическим инициализатором и выполняется одновременно с загрузкой в память скрипта pipeline.
2) Максимум кода выносим в src, т.к. его легко тестировать и код можно писать как будто это не скрипт сборки, а приложение для CI\CD.
Собственно именно так к коду и нужно относится.
3) создаем проект. Логичным выглядит использование Gradle, т.к у нас все-таки Groovy.
Вы можете вызывать в своем коде Java и Groovy стандартные библиотеки, следовательно, чтобы подсветка синтаксиса и автоподстановка работали, надо соответствующим образом настроить проект.
Кроме того стоит добавить в проект зависимости Jenkins Core и плагины. Я говорил, что для шагов pipeline нет соответствующих классов, но кроме шагов у Jenkins есть открытое и полузакрытое API, которое можно вызвать из PIpeline.
Примеры:
com.cloudbees.groovy.cps.NonCPS
hudson.AbortException
Полный список библиотек в сети я не нашел, да и не нужен он, я гуглил по нужным мне классам. Если нужен моя заготовка - обращайтесь.
4) подключаем в проект GDSL https://www.tothenew.com/blog/gdsl-awesomeness-introduction-to-gdsl-in-intellij-idea/
Это механизм, позволяющий объявить контекст, это может быть контекст Jenkins scripts, контекст определенного класса, контекст Groovy closure, контекст Closure внутри другого Closure в определенным именем... и объявить какие методы и свойства доступны в этом контексте.
IDEA умеет подсвечивать синтактсис и автодополнять код по GDSL https://st-g.de/2016/08/jenkins-pipeline-autocompletion-in-intellij
Есть одна тонкость - файл нужно скачивать из своего Jenkins, чтобы файл с описанием DSL включал методы из ваших плагинов Jenkins.
При этом гарантии, что полученный GDSL будет включать все нет, это не так, т.к. зависит от добросовестности авторов плагинов. Я файл дописывал, добавляя недостающее и корректируя типы.
4) с GDSL есть одна засада. Он работает в скриптах с DSL кодом, но не в исходниках из папки src. Т.к контест другой. А часто ваши классы будут обвертками над вызовами pipeline DSL.
Решение есть - из скрипта передаем this, это контекст скрипта, он содержит все доступные DSL методы, в классе объявляем поле groovy.lang.Script script и модифицируем GDSL файл, добавляя все методы и свойства DSL в контекст класса groovy.lang.Script.
vars/process.groovy
...
def processor = new Processor(this)
...
src/Processor.groovy
@TupleConstructor
class Processor {
Script script
}
Раз уж заговорили про тесты расскажу про отладку и тестирование Jenkins pipeline. Для начала: отладка и тестирование Jenkins pipeline - это боль(((
Почему?
1) pipeline пишется на Groovy, а Groovy - это язык с динамической типизацией. Динамическая типизация хорошо подходит для небольших скриптов, но как только кода становится много - код сыпется, править его становится страшно.
Да, есть аннотация @groovy.transform.CompileStatic, но см. п.2
2) pipeline пишется не просто на Groovy, а на Groovy DSL. Стандартный Jenkins предоставляет ряд команд, они же шаги, плюс их число расширяется плагинами. Список см. https://www.jenkins.io/doc/pipeline/steps/
Т.е классов, описывающих синтаксис pipeline просто нет.
3) чтобы протестировать pipeline вживую нужно время. На ожидание свободного Jenkins slave, на скачивание кода пайплайн. Т.е. цикл обратной связи получается большим.
Что же можно сделать.
1) как только пайплайн перестает быть элементарным или появляется общий код - выносим его в shared library: https://www.jenkins.io/doc/book/pipeline/shared-libraries/
Она может содержать как DSL - в папке vars, так и обычные Groovy классы в папке src. А на обычные классы можно и нужно писать модульные тесты.
Как вариант - пайплайн и библиотека могут быть в одном репозитории, тогда библиотеку из pipeline можно загрузить так:
library identifier: "selflib@${scm.branches[0]}", retriever: legacySCM(scm)
В скрипте pipeline, который указан в джобе, оставляем минимум кода. Почему это полезно кроме упрощения тестирования - код в pipeline по сути является статическим инициализатором и выполняется одновременно с загрузкой в память скрипта pipeline.
2) Максимум кода выносим в src, т.к. его легко тестировать и код можно писать как будто это не скрипт сборки, а приложение для CI\CD.
Собственно именно так к коду и нужно относится.
3) создаем проект. Логичным выглядит использование Gradle, т.к у нас все-таки Groovy.
Вы можете вызывать в своем коде Java и Groovy стандартные библиотеки, следовательно, чтобы подсветка синтаксиса и автоподстановка работали, надо соответствующим образом настроить проект.
Кроме того стоит добавить в проект зависимости Jenkins Core и плагины. Я говорил, что для шагов pipeline нет соответствующих классов, но кроме шагов у Jenkins есть открытое и полузакрытое API, которое можно вызвать из PIpeline.
Примеры:
com.cloudbees.groovy.cps.NonCPS
hudson.AbortException
Полный список библиотек в сети я не нашел, да и не нужен он, я гуглил по нужным мне классам. Если нужен моя заготовка - обращайтесь.
4) подключаем в проект GDSL https://www.tothenew.com/blog/gdsl-awesomeness-introduction-to-gdsl-in-intellij-idea/
Это механизм, позволяющий объявить контекст, это может быть контекст Jenkins scripts, контекст определенного класса, контекст Groovy closure, контекст Closure внутри другого Closure в определенным именем... и объявить какие методы и свойства доступны в этом контексте.
IDEA умеет подсвечивать синтактсис и автодополнять код по GDSL https://st-g.de/2016/08/jenkins-pipeline-autocompletion-in-intellij
Есть одна тонкость - файл нужно скачивать из своего Jenkins, чтобы файл с описанием DSL включал методы из ваших плагинов Jenkins.
При этом гарантии, что полученный GDSL будет включать все нет, это не так, т.к. зависит от добросовестности авторов плагинов. Я файл дописывал, добавляя недостающее и корректируя типы.
4) с GDSL есть одна засада. Он работает в скриптах с DSL кодом, но не в исходниках из папки src. Т.к контест другой. А часто ваши классы будут обвертками над вызовами pipeline DSL.
Решение есть - из скрипта передаем this, это контекст скрипта, он содержит все доступные DSL методы, в классе объявляем поле groovy.lang.Script script и модифицируем GDSL файл, добавляя все методы и свойства DSL в контекст класса groovy.lang.Script.
vars/process.groovy
...
def processor = new Processor(this)
...
src/Processor.groovy
@TupleConstructor
class Processor {
Script script
}
Pipeline Steps Reference
Jenkins – an open source automation server which enables developers around the world to reliably build, test, and deploy their software
Может возникнуть вопрос - что такое groovy.lang.Script? По сути это аналог Object для Groovy скриптов. Да, в Groovy код может быть не только в классах, но и в скриптах https://docs.groovy-lang.org/latest/html/documentation/#_scripts_versus_classes
5) В API своих классов не используем def, всегда объявляем типы явно.
def - это как var в Java, но опаснее, т.к в отличие от Java его можно использовать везде вместо указания типа. Как по мне - использовать def можно только для локальных переменных, т.е по сути я за подход Java.
6) кроме подсветки синтаксиса рекомендую периодически вызывать Inspect Code в IDEA, а лучше повесить его на commit. Не все проверки по умолчанию актуальны для Jenkins pipeline кода, лишние можно отключить.
7) ну и возвращаясь к тестам - по максимуму покрываем код в src тестами. Можно использовать JUnit и Mockito. Тесты при этом пишем на Groovy, чтобы воспользоваться преимуществом компактного синтаксиса Groovy.
to be continued
#devops #ci #unittests #jenkins #groovy
5) В API своих классов не используем def, всегда объявляем типы явно.
def - это как var в Java, но опаснее, т.к в отличие от Java его можно использовать везде вместо указания типа. Как по мне - использовать def можно только для локальных переменных, т.е по сути я за подход Java.
6) кроме подсветки синтаксиса рекомендую периодически вызывать Inspect Code в IDEA, а лучше повесить его на commit. Не все проверки по умолчанию актуальны для Jenkins pipeline кода, лишние можно отключить.
7) ну и возвращаясь к тестам - по максимуму покрываем код в src тестами. Можно использовать JUnit и Mockito. Тесты при этом пишем на Groovy, чтобы воспользоваться преимуществом компактного синтаксиса Groovy.
to be continued
#devops #ci #unittests #jenkins #groovy
Продолжим про тестирование кода джобов Jenkins.
Что еще у нас есть для тестирования.
8) JenkinsPipelineUnit - https://github.com/jenkinsci/JenkinsPipelineUnit По сути набор моков для запуска кода pipeline.
Что может:
а) запуск пайплайн из файла и из строки
б) передача параметров и переменных среды
в) проверка статуса выполнения джобы
г) моки для ряда методов pipeline
д) загрузка shared library
е) возможность добавлять свои моки на команды pipeline или конкретные вызовы sh
ж) печать стектрейса выполнения pipeline
з) сравнение стректрейсов, поиск по вхождению - можно искать были ли выполнена та или иная команда
Из мелких косяков - требует наследования тестового класса от BasePipelineTest, что вышло из моды с появлением Unit 4)))
Из более крупных косяков - по умолчанию многие команды Jenkins DSL не замоканы, при появлении такой команды джоба падает.
То что падает - это правильно, мы же тестируем pipeline. Но часто приходится писать свои mock, примеры: readYaml, readProperties, findFiles.
Mock по умолчанию - ничего не делать. echo выводит данные в лог на машине разработчика.
Могу рекомендовать с ремаркой - моки придется дописывать.
9) Jenkins Test Harness - https://www.jenkins.io/doc/developer/testing/,
Это интеграционное тестирование pipeline. В документации фреймворк предлагается для тех, кто разрабатывает Jenkins или плагины для него.
Можно ли использовать для тестирования своего pipeline и shared libraries - вопрос, дам на него ответ позже.
Коммиты в репозитории есть с 2016 года, но в документации по ссылке выше до сих пор встречаются TODO.
Подключение к тестам в примерах происходит через Rule из JUnit 4 - что тоже намекает.
Что он может:
а) б) в) из списка выше
г) мок для загрузки из SCM
д) проверка записей в логе - как я понял, это в большинстве случаев будет заменой Assert
е) загрузка файлов из среды разработки в workspace
Пока рекомендовать не могу, буду исследовать.
10) com.mkobit.jenkins.pipelines.shared-library - https://github.com/mkobit/jenkins-pipeline-shared-libraries-gradle-plugin,
Это плагин Gradle для разработки shared libraries. Включает в себя два предыдущих фреймворка. Есть тестовый репо https://github.com/mkobit/jenkins-pipeline-shared-library-example, если взять его как основу для своего проекта - получите из коробки подключение ряда библиотек Jenkins для declarative pipeline, некую версию gdsl и готовый проект, который содержит модульные и интеграционные тесты и проходит build.
Выглядит интересно для начала разработки, я к сожалению в свое время его упустил, по сути сделав аналогичный каркас)
Причем для разработки scripted pipeline мой каркас подходит лучше)
Пока рекомендовать не могу, учитывая комментарии выше.
11) любые тесты не на 100% заменяют запуск с реальными интеграциями. Как организовать интеграционное и функциональное тестирование pipeline, что для этого нужно?
а) создаем или копируем тестовый Java проект, который будем собирать. Ключевое требование - небольшой размер кода, чтобы сборка была быстрой и максимальное использование фичей pipeline. Использование настоящих проектов - плохо, т.к. создаются левые tags, build statuses, что может вводить разработчиков в заблуждение
б) тестовые джобы на Jenkins для всех созданных вами pipeline. Можно даже создать джобу, запускающую в параллель все эти джобы
в) тестовый проект SonarQube
г) тестовые репозитории в Nexus\Artifactory
д) тестовый проект на вашем Git сервере если джобы что-то делают в Git
е) важно: описываем в документации чек-лист - что и когда нужно тестировать при внесении изменений в pipeline
ж) придеживаемся описанных нами правил, это важно)
#devops #ci #unittests #jenkins #groovy
Что еще у нас есть для тестирования.
8) JenkinsPipelineUnit - https://github.com/jenkinsci/JenkinsPipelineUnit По сути набор моков для запуска кода pipeline.
Что может:
а) запуск пайплайн из файла и из строки
б) передача параметров и переменных среды
в) проверка статуса выполнения джобы
г) моки для ряда методов pipeline
д) загрузка shared library
е) возможность добавлять свои моки на команды pipeline или конкретные вызовы sh
ж) печать стектрейса выполнения pipeline
з) сравнение стректрейсов, поиск по вхождению - можно искать были ли выполнена та или иная команда
Из мелких косяков - требует наследования тестового класса от BasePipelineTest, что вышло из моды с появлением Unit 4)))
Из более крупных косяков - по умолчанию многие команды Jenkins DSL не замоканы, при появлении такой команды джоба падает.
То что падает - это правильно, мы же тестируем pipeline. Но часто приходится писать свои mock, примеры: readYaml, readProperties, findFiles.
Mock по умолчанию - ничего не делать. echo выводит данные в лог на машине разработчика.
Могу рекомендовать с ремаркой - моки придется дописывать.
9) Jenkins Test Harness - https://www.jenkins.io/doc/developer/testing/,
Это интеграционное тестирование pipeline. В документации фреймворк предлагается для тех, кто разрабатывает Jenkins или плагины для него.
Можно ли использовать для тестирования своего pipeline и shared libraries - вопрос, дам на него ответ позже.
Коммиты в репозитории есть с 2016 года, но в документации по ссылке выше до сих пор встречаются TODO.
Подключение к тестам в примерах происходит через Rule из JUnit 4 - что тоже намекает.
Что он может:
а) б) в) из списка выше
г) мок для загрузки из SCM
д) проверка записей в логе - как я понял, это в большинстве случаев будет заменой Assert
е) загрузка файлов из среды разработки в workspace
Пока рекомендовать не могу, буду исследовать.
10) com.mkobit.jenkins.pipelines.shared-library - https://github.com/mkobit/jenkins-pipeline-shared-libraries-gradle-plugin,
Это плагин Gradle для разработки shared libraries. Включает в себя два предыдущих фреймворка. Есть тестовый репо https://github.com/mkobit/jenkins-pipeline-shared-library-example, если взять его как основу для своего проекта - получите из коробки подключение ряда библиотек Jenkins для declarative pipeline, некую версию gdsl и готовый проект, который содержит модульные и интеграционные тесты и проходит build.
Выглядит интересно для начала разработки, я к сожалению в свое время его упустил, по сути сделав аналогичный каркас)
Причем для разработки scripted pipeline мой каркас подходит лучше)
Пока рекомендовать не могу, учитывая комментарии выше.
11) любые тесты не на 100% заменяют запуск с реальными интеграциями. Как организовать интеграционное и функциональное тестирование pipeline, что для этого нужно?
а) создаем или копируем тестовый Java проект, который будем собирать. Ключевое требование - небольшой размер кода, чтобы сборка была быстрой и максимальное использование фичей pipeline. Использование настоящих проектов - плохо, т.к. создаются левые tags, build statuses, что может вводить разработчиков в заблуждение
б) тестовые джобы на Jenkins для всех созданных вами pipeline. Можно даже создать джобу, запускающую в параллель все эти джобы
в) тестовый проект SonarQube
г) тестовые репозитории в Nexus\Artifactory
д) тестовый проект на вашем Git сервере если джобы что-то делают в Git
е) важно: описываем в документации чек-лист - что и когда нужно тестировать при внесении изменений в pipeline
ж) придеживаемся описанных нами правил, это важно)
#devops #ci #unittests #jenkins #groovy
GitHub
GitHub - jenkinsci/JenkinsPipelineUnit: Framework for unit testing Jenkins pipelines
Framework for unit testing Jenkins pipelines . Contribute to jenkinsci/JenkinsPipelineUnit development by creating an account on GitHub.
Всем привет!
Практически не писал про Kotlin, хотя он есть в названии канала. Надо исправляться.
Первый вопрос, который возникает при упоминании Kotlin - зачем он нужен, есть же Java?
Отвечаю:
1) Null safety - на уровне объявления типа мы говорим, допускает поле null или нет. Т.об. можно избежать NPE в Kotlin коде. Почему оговорка про Kotlin код - если передать значение в Java код - там работают правила Java
https://kotlinlang.org/docs/null-safety.html
2) встроенная в язык иммутабельность - var и val. При объявлении свойства или локальной переменной мы обязаны указать может ли оно меняться. В отличие от Java где объевление const опционально и его можно забыть
https://kotlinlang.org/docs/properties.html#delegated-properties
3) гора синтаксического сахара. Многие относятся к сахару пренебрежительно, ну типа и без него можно написать хороший код, это баловство. Да, можно написать хороший код практически на любом языке. Но я бы сместил акцент на читаемость. В основном мы код читаем, а не пишем - свой или чужой. И при чтении краткость помогает. Конечно, при условии что синтаксис языка тебе понятен) Тут я бы противопоставил Kotlin и Scala, т.к. Scala исходя из моего опыта подталкивает разработчика к тому, чтобы написать такую одну строчку кода, чтобы с первого раза никто не понял - что она делает. Этот подход - зло, если надо проектом работает более одного человека. А т.к. все меняется - даже один человек может уволится или забросить код, то такие строчки - всегда зло.
Ссылку на спецификацию языка давать не буду - можно ссылаться на все разделы: свойства классов, отсутствие ;, можно опускать {} для простых классов, упрощенный синтаксис primary конструктора, отсутствие new, single expression functions, data классы (хотя тут Java 17 подтянулась), elvis оператор, ranges, функции области видимости, repeat, алиасы для import, print, функция main без класса ...
Часть этих фичей дает Lombok, по некоторым Java потихоньку догоняет Kotlin. Но плюс Kotlin в том, что у него их больше и они предоставляются на чистом JDK 8+
4) корутины - как альтернативный подход к многопоточности, когда код выглядит однопоточным, но выполняется параллельно. Корутины - отдельная большая тема, я к ней вернусь. Для начала могу сказать только одно - сначала изучите, потом применяйте. Это везде важно, но с корутинами особенно)
https://kotlinlang.org/docs/coroutines-overview.html
5) единообразие в конструкциях языка. Примеры: все есть классы, нет неявных преобразований, можно переопределять операции (т.е. плюсовать не только String), функция всегда возвращает ответ, var\val сюда же. Это упрощает обучение языку с нуля, сообственно такую цель ставили авторы языка. Ну и еще упростить работу коллегам, отвечающим за статический анализ кода в IDEA)
6) легкость создания DSL на Kotlin. Фактически за счет двух кусков синтаксического сахара: - возможность опустить круглые скобки у метода и возможность inline передать одну функцию в другую мы получаем вот такой код
func1 {
code of func 2
}
#kotlin #java
Практически не писал про Kotlin, хотя он есть в названии канала. Надо исправляться.
Первый вопрос, который возникает при упоминании Kotlin - зачем он нужен, есть же Java?
Отвечаю:
1) Null safety - на уровне объявления типа мы говорим, допускает поле null или нет. Т.об. можно избежать NPE в Kotlin коде. Почему оговорка про Kotlin код - если передать значение в Java код - там работают правила Java
https://kotlinlang.org/docs/null-safety.html
2) встроенная в язык иммутабельность - var и val. При объявлении свойства или локальной переменной мы обязаны указать может ли оно меняться. В отличие от Java где объевление const опционально и его можно забыть
https://kotlinlang.org/docs/properties.html#delegated-properties
3) гора синтаксического сахара. Многие относятся к сахару пренебрежительно, ну типа и без него можно написать хороший код, это баловство. Да, можно написать хороший код практически на любом языке. Но я бы сместил акцент на читаемость. В основном мы код читаем, а не пишем - свой или чужой. И при чтении краткость помогает. Конечно, при условии что синтаксис языка тебе понятен) Тут я бы противопоставил Kotlin и Scala, т.к. Scala исходя из моего опыта подталкивает разработчика к тому, чтобы написать такую одну строчку кода, чтобы с первого раза никто не понял - что она делает. Этот подход - зло, если надо проектом работает более одного человека. А т.к. все меняется - даже один человек может уволится или забросить код, то такие строчки - всегда зло.
Ссылку на спецификацию языка давать не буду - можно ссылаться на все разделы: свойства классов, отсутствие ;, можно опускать {} для простых классов, упрощенный синтаксис primary конструктора, отсутствие new, single expression functions, data классы (хотя тут Java 17 подтянулась), elvis оператор, ranges, функции области видимости, repeat, алиасы для import, print, функция main без класса ...
Часть этих фичей дает Lombok, по некоторым Java потихоньку догоняет Kotlin. Но плюс Kotlin в том, что у него их больше и они предоставляются на чистом JDK 8+
4) корутины - как альтернативный подход к многопоточности, когда код выглядит однопоточным, но выполняется параллельно. Корутины - отдельная большая тема, я к ней вернусь. Для начала могу сказать только одно - сначала изучите, потом применяйте. Это везде важно, но с корутинами особенно)
https://kotlinlang.org/docs/coroutines-overview.html
5) единообразие в конструкциях языка. Примеры: все есть классы, нет неявных преобразований, можно переопределять операции (т.е. плюсовать не только String), функция всегда возвращает ответ, var\val сюда же. Это упрощает обучение языку с нуля, сообственно такую цель ставили авторы языка. Ну и еще упростить работу коллегам, отвечающим за статический анализ кода в IDEA)
6) легкость создания DSL на Kotlin. Фактически за счет двух кусков синтаксического сахара: - возможность опустить круглые скобки у метода и возможность inline передать одну функцию в другую мы получаем вот такой код
func1 {
code of func 2
}
#kotlin #java
Kotlin Help
Null safety | Kotlin
Всем привет!
Продолжим про Kotlin.
Есть ли у него минусы? А точнее какие могут быть косяки в коде Kotlin, вызванные в том числе неправильным использованием языка.
Немного накину по своему опыту.
1) злоупотребление контекстными функциями типа also.
https://kotlinlang.org/docs/scope-functions.html
На примере also - он нужен если в коде используются поля и методы объекта контекста - it. Если можно просто две написать последовательные строчки кода - лучше так и сделать.
obj.doSomething().also { doAnotherImportantBusinessLogic() } - плохо
obj.doSomething()
doAnotherImportantBusinessLogic()
- хорошо
2) злоупотребление Extension functions
https://kotlinlang.org/docs/extensions.html
Extension functions конечно крутая штука, но она ухудшает читаемость кода, т.к. код класса размазывается по нескольким файлам. Т.е. использовать ее нужно только если очень нужно)
И если очень нужно и это extension библиотечного типа - поместить его в класс, а не как top level функцию с пакетной видимостью.
3) использование упрощенного синтаксиса для объявления метода + контекстные функции приводят к тому, что Single expression function превращается в Very Big Expression Function и, соответственно, ухудшается читаемость кода метода.
fun transform(x: String): Result<Type1, Type2> = Result(doSomething()).also {
code
other code
another code
more code
....
}
Собственно похожая проблема будет если злоупотреблять method chaining
obj.prepare(...).recieve().handleSuccees(..).log(...).postHandle(..)...
Хочу обратить внимание на два момента - злоупотребление=применение ради применения и читаемость кода. Я думаю это два хороших критерия для ответа на вопрос - стоит ли применять ту или иную фичу.
#kotlin #cleancode
Продолжим про Kotlin.
Есть ли у него минусы? А точнее какие могут быть косяки в коде Kotlin, вызванные в том числе неправильным использованием языка.
Немного накину по своему опыту.
1) злоупотребление контекстными функциями типа also.
https://kotlinlang.org/docs/scope-functions.html
На примере also - он нужен если в коде используются поля и методы объекта контекста - it. Если можно просто две написать последовательные строчки кода - лучше так и сделать.
obj.doSomething().also { doAnotherImportantBusinessLogic() } - плохо
obj.doSomething()
doAnotherImportantBusinessLogic()
- хорошо
2) злоупотребление Extension functions
https://kotlinlang.org/docs/extensions.html
Extension functions конечно крутая штука, но она ухудшает читаемость кода, т.к. код класса размазывается по нескольким файлам. Т.е. использовать ее нужно только если очень нужно)
И если очень нужно и это extension библиотечного типа - поместить его в класс, а не как top level функцию с пакетной видимостью.
3) использование упрощенного синтаксиса для объявления метода + контекстные функции приводят к тому, что Single expression function превращается в Very Big Expression Function и, соответственно, ухудшается читаемость кода метода.
fun transform(x: String): Result<Type1, Type2> = Result(doSomething()).also {
code
other code
another code
more code
....
}
Собственно похожая проблема будет если злоупотреблять method chaining
obj.prepare(...).recieve().handleSuccees(..).log(...).postHandle(..)...
Хочу обратить внимание на два момента - злоупотребление=применение ради применения и читаемость кода. Я думаю это два хороших критерия для ответа на вопрос - стоит ли применять ту или иную фичу.
#kotlin #cleancode
Привет!
Большинство DevOps pipeline, что я видел, мягко говоря не быстрые. Даже в теории чтобы просто докатить commit до ПРОМа нужны часы. Возможно у компаний с лучшими практиками DevOps это десятки минут. Можно ли быстрее?
Теоретически - да, и существует даже действующая модель:
https://medium.com/darklang/how-dark-deploys-code-in-50ms-771c6dd60671
https://docs.darklang.com/tutorials/first-dark-application#open-the-editor
В чем суть: ребята объединили язык, среду разработки, среду выполнения, хранилище данных и DevOps включая запуск тестов в одном продукте. В язык и среду встроены feature toggles, т.е. любое несовместимое изменение должно включаться по своему feature toggle. Естественно в выключенном состоянии. Это касается кода, таблиц в БД, новых библиотек. Таблицы и библиотеки получают новые версии, использование которых нужно явно включать. Сохранение срочки кода = commit. Невалидный код сохранить нельзя. Коммит идет сразу на ПРОМ после прогона тестов. Возможности языка ограничены, т.к. среда управляет инфраструктурой. По сути получаем serverless. Среда хранит логи и трасировки запросов, в т.ч. невалидные запросы, для последних можно быстро создать обработчик, т.е. получаем trace based development.
Мои выводы по результатам краткого знакомства.
1) концепт интересный, я бы даже сказал крутой
2) разработка в браузере смущает
3) возможности языка ограничены, но возможно спасают библиотеки
4) и главное - непонятно как с этим работать более чем одному разработчику и\или если число строк кода более 100. Если разработчики где-то будут пересекаться по коду - есть риск, что они не будут знать про чужие feature toggles и проверять их не будут. Или если фича большая, включает десятки функций и обработчиков, а у каждого свой feature toggle, спроектировать и проверить их в нужном порядке не совсем элементарная задача. В обоих случаях получаем feature toggle hell.
Но концепт красивый)
#devops #lang #concepts
Большинство DevOps pipeline, что я видел, мягко говоря не быстрые. Даже в теории чтобы просто докатить commit до ПРОМа нужны часы. Возможно у компаний с лучшими практиками DevOps это десятки минут. Можно ли быстрее?
Теоретически - да, и существует даже действующая модель:
https://medium.com/darklang/how-dark-deploys-code-in-50ms-771c6dd60671
https://docs.darklang.com/tutorials/first-dark-application#open-the-editor
В чем суть: ребята объединили язык, среду разработки, среду выполнения, хранилище данных и DevOps включая запуск тестов в одном продукте. В язык и среду встроены feature toggles, т.е. любое несовместимое изменение должно включаться по своему feature toggle. Естественно в выключенном состоянии. Это касается кода, таблиц в БД, новых библиотек. Таблицы и библиотеки получают новые версии, использование которых нужно явно включать. Сохранение срочки кода = commit. Невалидный код сохранить нельзя. Коммит идет сразу на ПРОМ после прогона тестов. Возможности языка ограничены, т.к. среда управляет инфраструктурой. По сути получаем serverless. Среда хранит логи и трасировки запросов, в т.ч. невалидные запросы, для последних можно быстро создать обработчик, т.е. получаем trace based development.
Мои выводы по результатам краткого знакомства.
1) концепт интересный, я бы даже сказал крутой
2) разработка в браузере смущает
3) возможности языка ограничены, но возможно спасают библиотеки
4) и главное - непонятно как с этим работать более чем одному разработчику и\или если число строк кода более 100. Если разработчики где-то будут пересекаться по коду - есть риск, что они не будут знать про чужие feature toggles и проверять их не будут. Или если фича большая, включает десятки функций и обработчиков, а у каждого свой feature toggle, спроектировать и проверить их в нужном порядке не совсем элементарная задача. В обоих случаях получаем feature toggle hell.
Но концепт красивый)
#devops #lang #concepts
Medium
How Dark deploys code in 50ms
Speed of developer iteration is the single most important factor in how quickly a technology company can move. In Dark, deploys take 50ms!
Всем привет!
Почему Kotlin можно использовать вместе с Java?
1) Код на Kotlin комплиируется в тот же байткод, что и Java, компилятор Kotlin может что-то оптимизировать, но в любом случае он обязан придерживаться спецификации байткода определенной версии JVM. Т.е. в runtime проекта, в который добавили Kotlin, ничего необычного появиться не может.
По сути Kotlin в runtime-е = плюс несколько jar-ников в проекте.
2) Разработчики Kotlin изначально ставили себе цель - интероперабельность с Java. Об этом можно понять из их интервью и из документации языка. Ясное дело, при работе с объектами Kotlin в Java и наоборот действуют правила host языка. Поэтому везде, где есть особенности при взаимодействии с Java, например, null safety теряется при передаче в Java код - в документации есть оговорки про Java. https://kotlinlang.org/docs/null-safety.html#nullable-types-and-non-null-types
Есть специальные конструкции для корректной работы в Java типа @JvmStatic
https://www.baeldung.com/kotlin/jvmstatic-annotation
3) Фреймоворки и библиотеки Java. Kotlin уже давно достиг того уровня распространнености, когда создатели фреймворков вынуждены допиливать их для совместимости. Пример: Spring 5.0, одна из основных фич - совместимость с Kotlin.
https://spring.io/blog/2017/01/04/introducing-kotlin-support-in-spring-framework-5-0
Обратите внимание на время появления записи.
Также любые примеры кода на двух языках:
https://docs.spring.io/spring-framework/docs/current/reference/html/core.html
Опять же из-за расспространнености Kotlin большинство проблем гуглится. Об известных мне проблемах напишу отдельно
4) Подддержка в tooling. Идеальный вариант - не менять тулзы, использовать те же, что и для Java.
IDEA - ну с ней все ясно)
Gradle\Maven + На Gradle даже можно писать код сборки на KotlinDSL.
JaCoCO +
SonarQube + Как я понимаю, в базовом плагине SonarKotlin правил мало, но можно добавить правила Detekt https://github.com/detekt/sonar-kotlin
SpotBugs +\- В плагине для Kotlin поддержка добавлена https://github.com/spotbugs/sonar-findbugs/issues/373
в standalone - нет https://github.com/spotbugs/spotbugs/issues/573
В последней таске есть интересный комменты от maintener-ов проекта. Да, байткод у Kotlin валидный. Но статическому анализатору во-первых сложно сопоставить байткод с кодом, а во-вторых анализатор ломается на некотором автотически сгенерированном байткоде, который не делает Java компилятор.
JMH +
Lombok +- https://kotlinlang.org/docs/lombok.html#supported-annotations Касается интероперабельности, т.е работа с Lombok Java классами в Kotlin.
SAST:
Checkmarx + https://checkmarx.atlassian.net/wiki/spaces/KC/pages/1809220013/9.2.0+Supported+Code+Languages+and+Frameworks
find-sec-bugs + https://github.com/find-sec-bugs/find-sec-bugs/releases
Checkstyle - проверки тесно завязаны на синтаксис языка https://checkstyle.sourceforge.io/style_configs.html, плагина\аддона для Kotlin нет и возможно
не появится. Заменяется https://pinterest.github.io/ktlint/
Ну и несколько ложек дегтя.
AspectJ compile time weaving не поддерживается:
https://youtrack.jetbrains.com/issue/KT-43476/Provide-aspect-aware-kotlin-compiler-similar-to-AspectJs-ajc
Зато можно использовать Spring AOP https://stackoverflow.com/questions/64281701/spring-aop-aspectj-with-kotlin-and-gradle-i-cant-get-it-to-work
или функциональный подход вместо аспектов https://medium.com/dont-code-me-on-that/aspect-oriented-programming-in-kotlin-95ff8598913
PMD - работа идет https://github.com/pmd/pmd/issues/419, пока она идет - заменяется на Detekt и SonarQube.
Что я забыл из tooling?
Следующий вопрос - как лучше использовать Kotlin с Java. Варианты:
1) делать на Kotlin новые микросервисы. Наверное идеальный вариант, т.к проблем интероперабельности просто нет)
2) делать на Kotlin отдельные модули в существующем проекте. Код получается более целостным, проще настройки build tool
3) смешивать в одном модуле Kotlin и Java - не самый лучший вариант, в коде сложнее разбираться
4) начать с тестов на Kotlin. Вариант для сверхосторожных, я больших плюсов в нем не вижу.
#java #kotlin #spring
Почему Kotlin можно использовать вместе с Java?
1) Код на Kotlin комплиируется в тот же байткод, что и Java, компилятор Kotlin может что-то оптимизировать, но в любом случае он обязан придерживаться спецификации байткода определенной версии JVM. Т.е. в runtime проекта, в который добавили Kotlin, ничего необычного появиться не может.
По сути Kotlin в runtime-е = плюс несколько jar-ников в проекте.
2) Разработчики Kotlin изначально ставили себе цель - интероперабельность с Java. Об этом можно понять из их интервью и из документации языка. Ясное дело, при работе с объектами Kotlin в Java и наоборот действуют правила host языка. Поэтому везде, где есть особенности при взаимодействии с Java, например, null safety теряется при передаче в Java код - в документации есть оговорки про Java. https://kotlinlang.org/docs/null-safety.html#nullable-types-and-non-null-types
Есть специальные конструкции для корректной работы в Java типа @JvmStatic
https://www.baeldung.com/kotlin/jvmstatic-annotation
3) Фреймоворки и библиотеки Java. Kotlin уже давно достиг того уровня распространнености, когда создатели фреймворков вынуждены допиливать их для совместимости. Пример: Spring 5.0, одна из основных фич - совместимость с Kotlin.
https://spring.io/blog/2017/01/04/introducing-kotlin-support-in-spring-framework-5-0
Обратите внимание на время появления записи.
Также любые примеры кода на двух языках:
https://docs.spring.io/spring-framework/docs/current/reference/html/core.html
Опять же из-за расспространнености Kotlin большинство проблем гуглится. Об известных мне проблемах напишу отдельно
4) Подддержка в tooling. Идеальный вариант - не менять тулзы, использовать те же, что и для Java.
IDEA - ну с ней все ясно)
Gradle\Maven + На Gradle даже можно писать код сборки на KotlinDSL.
JaCoCO +
SonarQube + Как я понимаю, в базовом плагине SonarKotlin правил мало, но можно добавить правила Detekt https://github.com/detekt/sonar-kotlin
SpotBugs +\- В плагине для Kotlin поддержка добавлена https://github.com/spotbugs/sonar-findbugs/issues/373
в standalone - нет https://github.com/spotbugs/spotbugs/issues/573
В последней таске есть интересный комменты от maintener-ов проекта. Да, байткод у Kotlin валидный. Но статическому анализатору во-первых сложно сопоставить байткод с кодом, а во-вторых анализатор ломается на некотором автотически сгенерированном байткоде, который не делает Java компилятор.
JMH +
Lombok +- https://kotlinlang.org/docs/lombok.html#supported-annotations Касается интероперабельности, т.е работа с Lombok Java классами в Kotlin.
SAST:
Checkmarx + https://checkmarx.atlassian.net/wiki/spaces/KC/pages/1809220013/9.2.0+Supported+Code+Languages+and+Frameworks
find-sec-bugs + https://github.com/find-sec-bugs/find-sec-bugs/releases
Checkstyle - проверки тесно завязаны на синтаксис языка https://checkstyle.sourceforge.io/style_configs.html, плагина\аддона для Kotlin нет и возможно
не появится. Заменяется https://pinterest.github.io/ktlint/
Ну и несколько ложек дегтя.
AspectJ compile time weaving не поддерживается:
https://youtrack.jetbrains.com/issue/KT-43476/Provide-aspect-aware-kotlin-compiler-similar-to-AspectJs-ajc
Зато можно использовать Spring AOP https://stackoverflow.com/questions/64281701/spring-aop-aspectj-with-kotlin-and-gradle-i-cant-get-it-to-work
или функциональный подход вместо аспектов https://medium.com/dont-code-me-on-that/aspect-oriented-programming-in-kotlin-95ff8598913
PMD - работа идет https://github.com/pmd/pmd/issues/419, пока она идет - заменяется на Detekt и SonarQube.
Что я забыл из tooling?
Следующий вопрос - как лучше использовать Kotlin с Java. Варианты:
1) делать на Kotlin новые микросервисы. Наверное идеальный вариант, т.к проблем интероперабельности просто нет)
2) делать на Kotlin отдельные модули в существующем проекте. Код получается более целостным, проще настройки build tool
3) смешивать в одном модуле Kotlin и Java - не самый лучший вариант, в коде сложнее разбираться
4) начать с тестов на Kotlin. Вариант для сверхосторожных, я больших плюсов в нем не вижу.
#java #kotlin #spring
Kotlin Help
Null safety | Kotlin
Всем привет!
Чтобы после моей предыдущей статьи о тестировании пайплайнов Jenkins не сложилось впечатления, что проблем нет и провалидированный IDEA, скомпилированный и оттестированный JUnit и Pipeline Unit тестами код сразу заработает в Jenkins - вот три больших ложки дегтя. Уточню, речь про scripted pipeline.
1) CPS. Детально что это за зверь написано тут https://www.jenkins.io/doc/book/pipeline/cps-method-mismatches/
Суть в том, что при выполнении код пайпа интерпретируется специальным образом, чтобы в любой момент на диске лежал актуальный слепок текущего состояния пайпа и можно было восстановить состояние после рестарта Jenkins. К слову, по моему опыту это не всегда работает, возможно пайпы кривые, возможно есть проблемы с плагинами. При этом далеко не весь вызываемый в runtime код можно так трасформировать, т.е. весь код делится на CPS и NonCPS. Не трансформируется Java standart library код, конструкторы и методы, помеченные @NonCPS. И есть правило - NonCPS код не может вызывать CPS код.
На модульном тесте это проверить невозможно, функционал PipelineUnit для этого по факту не работает.
2) Groovy DSL. Код пишется не на Groovy, а на Groovy DSL, а это две большие разницы) ну может не совсем большие, но точно разницы. Т.е. почитав доки по Groovy ты видишь там разные крутые фичи, думаешь - о, а у него есть плюсы по сравнению в Java, пробуешь их использовать - и облом. Вот некоторые примеры:
а) не работает with
б) не работают traits
в) не работает ссылка на метод через .& - используем {}. В принципе это даже более Groovish, но факт остается фактом
г) переопределять методы enum в пайпе нельзя https://issues.jenkins.io/browse/JENKINS-48722
д) использовать @MapConstructor тоже нельзя https://issues.jenkins.io/browse/JENKINS-45901
е) без аннотации map constructor тоже не работает
Как видно, на некоторые проблемы заведены баги, которые не решаются годами. Подозреваю, по двум причинам - разработчики фокусируются на declarative pipeline и трудоемкость исправления
3) Sandbox. В целях безопасности на большинстве нормально настроенных Jenkins включен режим Sandbox https://www.jenkins.io/doc/book/managing/script-approval/
Суть в том, что код запускается в песочнице, где разрешен вызов методов по whilelist. Есть возможность добавить в whitelist новые методы, но нужно апрувить каждый (!) метод. В зависимости от типа среды и компании это может быть трудоемко. Предположим, решили вы использовать новый Java DataTime API, написали 10 строк кода, вызвали пяток метод и все пять приходится апрувить. И узнаешь об этом также только когда выполнение кода дошло до нужного метода.
По моему опыту именно на разруливание этих трех проблем тратится максимум времени при отладке пайпа.
#devops #jenkins #unittest #debug
Чтобы после моей предыдущей статьи о тестировании пайплайнов Jenkins не сложилось впечатления, что проблем нет и провалидированный IDEA, скомпилированный и оттестированный JUnit и Pipeline Unit тестами код сразу заработает в Jenkins - вот три больших ложки дегтя. Уточню, речь про scripted pipeline.
1) CPS. Детально что это за зверь написано тут https://www.jenkins.io/doc/book/pipeline/cps-method-mismatches/
Суть в том, что при выполнении код пайпа интерпретируется специальным образом, чтобы в любой момент на диске лежал актуальный слепок текущего состояния пайпа и можно было восстановить состояние после рестарта Jenkins. К слову, по моему опыту это не всегда работает, возможно пайпы кривые, возможно есть проблемы с плагинами. При этом далеко не весь вызываемый в runtime код можно так трасформировать, т.е. весь код делится на CPS и NonCPS. Не трансформируется Java standart library код, конструкторы и методы, помеченные @NonCPS. И есть правило - NonCPS код не может вызывать CPS код.
На модульном тесте это проверить невозможно, функционал PipelineUnit для этого по факту не работает.
2) Groovy DSL. Код пишется не на Groovy, а на Groovy DSL, а это две большие разницы) ну может не совсем большие, но точно разницы. Т.е. почитав доки по Groovy ты видишь там разные крутые фичи, думаешь - о, а у него есть плюсы по сравнению в Java, пробуешь их использовать - и облом. Вот некоторые примеры:
а) не работает with
б) не работают traits
в) не работает ссылка на метод через .& - используем {}. В принципе это даже более Groovish, но факт остается фактом
г) переопределять методы enum в пайпе нельзя https://issues.jenkins.io/browse/JENKINS-48722
д) использовать @MapConstructor тоже нельзя https://issues.jenkins.io/browse/JENKINS-45901
е) без аннотации map constructor тоже не работает
Как видно, на некоторые проблемы заведены баги, которые не решаются годами. Подозреваю, по двум причинам - разработчики фокусируются на declarative pipeline и трудоемкость исправления
3) Sandbox. В целях безопасности на большинстве нормально настроенных Jenkins включен режим Sandbox https://www.jenkins.io/doc/book/managing/script-approval/
Суть в том, что код запускается в песочнице, где разрешен вызов методов по whilelist. Есть возможность добавить в whitelist новые методы, но нужно апрувить каждый (!) метод. В зависимости от типа среды и компании это может быть трудоемко. Предположим, решили вы использовать новый Java DataTime API, написали 10 строк кода, вызвали пяток метод и все пять приходится апрувить. И узнаешь об этом также только когда выполнение кода дошло до нужного метода.
По моему опыту именно на разруливание этих трех проблем тратится максимум времени при отладке пайпа.
#devops #jenkins #unittest #debug
Pipeline CPS Method Mismatches
Jenkins – an open source automation server which enables developers around the world to reliably build, test, and deploy their software
Всем привет!
Хочу вернуться к теме "правильных" модульных тестов и подчеркнуть пару важных моментов.
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). Эти школы кардинально...
Всем привет!
Наткнулся недавно на статью в двух частях о Java stacktrace.
Рекомендую!
Из статьи можно узнать:
1) насколько "дороже" возврат из метода через исключения по сравнению с return. Спойлер - сильно дороже
2) как можно съэкономить при создании Exception
3) Как зависит "стоимость" исключения от глубины вызовов. Спойлерить не буду, чтобы не быть кэпом)
4) Плюсы нового API для разбора исключения java.lang.StackWalker. Это Java 9
5) Сколько вызовов влезет в стек при стандартных размерах стека. Спойлер - it depends
Пример стектрейса для затравки: https://mattwarren.org/images/2016/12/Huge%20Java%20Stack%20Trace.png
6) Все возможные методы получения stacktrace у работающего и зависшего процесса. Спойлер - их много) Можно даже из консоли Windows\Linux
7) Немного о том, как работают профилировщики
8) И наконец как взломать JVM и украсть пароли из полей класса
https://habr.com/ru/company/jugru/blog/324932/
https://habr.com/ru/company/jugru/blog/325064/
#java #debug
Наткнулся недавно на статью в двух частях о Java stacktrace.
Рекомендую!
Из статьи можно узнать:
1) насколько "дороже" возврат из метода через исключения по сравнению с return. Спойлер - сильно дороже
2) как можно съэкономить при создании Exception
3) Как зависит "стоимость" исключения от глубины вызовов. Спойлерить не буду, чтобы не быть кэпом)
4) Плюсы нового API для разбора исключения java.lang.StackWalker. Это Java 9
5) Сколько вызовов влезет в стек при стандартных размерах стека. Спойлер - it depends
Пример стектрейса для затравки: https://mattwarren.org/images/2016/12/Huge%20Java%20Stack%20Trace.png
6) Все возможные методы получения stacktrace у работающего и зависшего процесса. Спойлер - их много) Можно даже из консоли Windows\Linux
7) Немного о том, как работают профилировщики
8) И наконец как взломать JVM и украсть пароли из полей класса
https://habr.com/ru/company/jugru/blog/324932/
https://habr.com/ru/company/jugru/blog/325064/
#java #debug
Хабр
Всё, что вы хотели знать о стек-трейсах и хип-дампах. Часть 1
Практика показала, что хардкорные расшифровки с наших докладов хорошо заходят, так что мы решили продолжать. Сегодня у нас в меню смесь из подходов к поиску и анализу ошибок и крэшей, приправленная...
Всем привет!
Сегодня речь пойдет не про разработку, а про одну малоизвестную фичу git.
Возможно кому-то пригодится.
Встречайте - git notes.
Что это такое?
notes, они же заметки дают возможность добавить какие-то данные к commit не меняя commit. Может возникнуть вопрос - есть же amend?
amend меняет commit, как файлы, так и его hash.
Формат заметок может быть любым, в т.ч. бинарным. Ограничений по размеру не нашел.
Где это может быть полезно?
В notes можно хранить данные о сборке из CI системы, информацию, о прошедшем код-ревью. В теории даже артифакты сборки можно хранить, но я этого не говорил)
Технически notes - это отдельные ветки с маcкой refs/notes/*
При этом каждая note связана с commit.
По умолчанию commit - текущий HEAD, ветка - refs/notes/commits.
notes из ветки по умолчанию отображаются в выводе git log.
Переключить текущую ветку можно в настройках
[core]
notesRef =
Заметки можно добавлять, удалять, обновлять, подробнее см. https://git-scm.com/docs/git-notes
Ветки с заметками можно мержить между собой, естественно, перед этим стоит продумать формат данных для избежания конфликтов.
Т.к. заметки хранятся в отдельной ветке, ее нужно отдельно пушить и пулить.
git push refs/notes/*
git fetch origin refs/notes/*:refs/notes/*
Также можно добавить ветки с notes в настройки git origin, и они будут забираться с сервера автоматически при git pull
git config --add remote.origin.fetch +refs/notes/*:refs/notes/*
На и наконец еще хорошая статья на эту тему: https://alblue.bandlem.com/2011/11/git-tip-of-week-git-notes.html
#git #devops
Сегодня речь пойдет не про разработку, а про одну малоизвестную фичу git.
Возможно кому-то пригодится.
Встречайте - git notes.
Что это такое?
notes, они же заметки дают возможность добавить какие-то данные к commit не меняя commit. Может возникнуть вопрос - есть же amend?
amend меняет commit, как файлы, так и его hash.
Формат заметок может быть любым, в т.ч. бинарным. Ограничений по размеру не нашел.
Где это может быть полезно?
В notes можно хранить данные о сборке из CI системы, информацию, о прошедшем код-ревью. В теории даже артифакты сборки можно хранить, но я этого не говорил)
Технически notes - это отдельные ветки с маcкой refs/notes/*
При этом каждая note связана с commit.
По умолчанию commit - текущий HEAD, ветка - refs/notes/commits.
notes из ветки по умолчанию отображаются в выводе git log.
Переключить текущую ветку можно в настройках
[core]
notesRef =
Заметки можно добавлять, удалять, обновлять, подробнее см. https://git-scm.com/docs/git-notes
Ветки с заметками можно мержить между собой, естественно, перед этим стоит продумать формат данных для избежания конфликтов.
Т.к. заметки хранятся в отдельной ветке, ее нужно отдельно пушить и пулить.
git push refs/notes/*
git fetch origin refs/notes/*:refs/notes/*
Также можно добавить ветки с notes в настройки git origin, и они будут забираться с сервера автоматически при git pull
git config --add remote.origin.fetch +refs/notes/*:refs/notes/*
На и наконец еще хорошая статья на эту тему: https://alblue.bandlem.com/2011/11/git-tip-of-week-git-notes.html
#git #devops
Bandlem
Git Tip of the Week: Git Notes - AlBlue’s Blog
Git Tip of the Week: Git Notes
Gtotw
2011
Git
Th...
Gtotw
2011
Git
Th...
Всем привет!
Снова про алгоритмы.
Представим ситуацию, когда для каждого нашего объекта нужно получить адрес, где его хранить. Это может быть сервер в случае шардированной БД или корзина в HashMap. Хорошим решением является функция, которая однозначно по уникальному идентификатору нашей сущности выдаёт адрес сервера. Альтернативой является хранение таблицы соответствия отдельно, в случае распределённой системы это будет отдельный микросервис, что означает +1 запрос и увеличение задержек. Тривиальной реализацией является простой алгоритм - остаток от деления на число серверов. Но у такого есть минус - при изменении числа серверов распределение объектов по серверам или корзинам сильно меняется. Мы же хотим равномерное распределение. Если перестроение HashMap в памяти обычно (!) не проблема, то в случае десятка серверов в сети - ещё какая проблема. Для решения этой проблемы есть алгоритм консистентного хэширования. Он не защищает от перераспределения данных, но фиксирует их размер.
https://jaidayo.livejournal.com/2645.html #algorithm #distributedsystem
P.S. HashMap данный алгоритм не использует, на размерах порядка 1 млн записей рекомендуется сразу задавать начальный размер равный предполагаемому умноженному на 1.5
P.P.S. Хозяйке на заметку: в nginx, при использовании его в качестве балансировщика включить консистентное вычисление хэша от URL запроса можно одной строкой hash $request_uri consistent
P...S А вот документация по использованию алгоритма в Openstack https://docs.openstack.org/swift/latest/ring_background.html
#algorithm
Снова про алгоритмы.
Представим ситуацию, когда для каждого нашего объекта нужно получить адрес, где его хранить. Это может быть сервер в случае шардированной БД или корзина в HashMap. Хорошим решением является функция, которая однозначно по уникальному идентификатору нашей сущности выдаёт адрес сервера. Альтернативой является хранение таблицы соответствия отдельно, в случае распределённой системы это будет отдельный микросервис, что означает +1 запрос и увеличение задержек. Тривиальной реализацией является простой алгоритм - остаток от деления на число серверов. Но у такого есть минус - при изменении числа серверов распределение объектов по серверам или корзинам сильно меняется. Мы же хотим равномерное распределение. Если перестроение HashMap в памяти обычно (!) не проблема, то в случае десятка серверов в сети - ещё какая проблема. Для решения этой проблемы есть алгоритм консистентного хэширования. Он не защищает от перераспределения данных, но фиксирует их размер.
https://jaidayo.livejournal.com/2645.html #algorithm #distributedsystem
P.S. HashMap данный алгоритм не использует, на размерах порядка 1 млн записей рекомендуется сразу задавать начальный размер равный предполагаемому умноженному на 1.5
P.P.S. Хозяйке на заметку: в nginx, при использовании его в качестве балансировщика включить консистентное вычисление хэша от URL запроса можно одной строкой hash $request_uri consistent
P...S А вот документация по использованию алгоритма в Openstack https://docs.openstack.org/swift/latest/ring_background.html
#algorithm
Livejournal
Консистентные хэши (Consistent hashing)
View more presentations from Anton. Download here. Зачем? Есть большой пласт технологий для создания распределенных систем: создание железных решений, распределенного ПО для управления системой, распределенных хранилищь, etc. Все это двигает grid технологии…
Всем привет. Немного про цену создания объектов в Java. Для короткоживущих объектов на последних версиях JVM выигрыш от переиспользования объектов про сравнению с созданием составляет пример 25%. Справедливости ради на Java 8 разница была в 40%, т.е garbage collection развивается. Описание эксперимента тут http://blog.vanillajava.blog/2022/09/java-is-very-fast-if-you-dont-create.html #java #performance
blog.vanillajava.blog
Java is Very Fast, If You Don’t Create Many Objects
You still have to watch how many objects you create. This article looks at a benchmark passing events over TCP/IP at 4 billion events per ...
С другой стороны Java = объекты. Сборщики мусора стали достаточно умными, чтобы запускаться только тогда, когда памяти не хватает, и достаточно быстро убирать короткоживущихе объекты. Можно выбрать сборщик мусора либо с минимальными паузами, либо с минимальным overhead по ресурсам. Про выбор можно почитать тут https://www.baeldung.com/java-choosing-gc-algorithm Перераспределение памяти по ходу работы программы и роста heap можно убрать установив одинаковые xms и xmx, тогда JRE заберёт эту память из системы «навсегда» при старте приложения. Если на сервере много памяти и вы уверены, что heap-а точно хватит на все время работы программы - есть фейковый GC, который имеет ровно одну фичу - падать когда память кончается) https://openjdk.org/jeps/318 Когда это все не работает - примерно на десятках миллионах RPS как в статье из примера выше) #java #gc #performance
Всем привет! Уже упоминал книгу Идеальная работа Роберта Мартина https://habr.com/ru/company/piter/blog/679378/ Дочитал до конца и вот более полный отзыв. Вначале о хорошем: главы про применение TDD на практических задачах с livecoding - must read. Хороша глава про принцип YAGNI - you aren't gonna need it, особенно про то, как скорость современных компьютеров сделала этот принцип более актуальным. Хорошо написано про структуру presentation layer для упрощения тестирования этого слоя. Шаблоны тестирования там же. Хорошо про вязкость процесса программирования - скорость сборки и прогона тестов. Интересные данные про влияние парного программирования на скорость разработки. Только из-за этого книгу могу рекомендовать к прочтению. Но есть на мой взгляд и минус. Сквозь всю книгу проходит тезис про ответственность программиста за код. Вообще говоря мысль правильная. За любую работу нужно отвечать и делать ее качественно. Смущают три момента. 1) пафос, с которым это подаётся. Сразу вспоминается «Даешь пятилетку за четыре года». Или такой персонаж как Тони Робинсон. Делай хорошо, не делай плохо. Да, все тезисы обоснованы, но я бы добавил больше юмора, практических примеров и меньше пафоса. 2) ко многим вещам ты приходишь с опытом. Поэтому требовать одинаково с сеньора и джуна нельзя. Автор про это говорит, один раз, и все. Для кого тогда эти главы? 3) для доказательства хороших идей подтасовки не нужны. Пример с программистами из Volkswagen, которые обманули программу измерения выхлопов, выглядит именно подтасовкой. Вкратце - чтобы пройти новый экологический стандарт для дизельных двигателей руководство компании дало указание разработчикам так настроить программу работы двигателя, чтобы в тестовом режиме все было ок, хотя по факту он выделял больше вредных веществ, чем разрешено. Но штука в том, что с внедрением этого стандарта старые машины продолжают ездить по улицам. И писать что из-за плохих программистов машины причиняют вред здоровью людей... ну такое. Но, повторюсь - читайте главы про TDD, простоту кода, тестирования БД и UI, парное программирование и настройку проекта. #books #tdd
Хабр
Идеальная работа. Программирование без прикрас
Привет, Хаброжители! В книге «Идеальная работа. Программирование без прикрас» легендарный Роберт Мартин (Дядюшка Боб) создал исчерпывающее руководство по хорошей работе для каждого программиста....