First принципы - Independent
#Independent (независимость) тестов - тоже очень важная штука. Имеется в виду независимость их как друг от друга, так и от окружения, в котором они запускаются. Если они зависимы друг от друга, то для запуска какого-то определенного теста придется запускать из скопом, что увеличивает время запуска (а это плохо). Если зависимы от окружения - значит где-то они не будут запускаться, либо где-то будут падать, чем начнут создавать проблемы либо вам, либо вашим коллегам. Так же нельзя забывать, что запуск тестов в многопоточной среде - это тоже запуск в определенном окружении. И если сами тестовые классы имеют stateful-природу (имеют состояние), то тесты получаются как зависимыми от этого состояния так и друг от друга. А потом еще и нарушают #Repeatable принцип, если начнут иногда падать при запуске с помощью многопоточного Runner’а, к которому многие проекты все равно приходят чтоб сделать запуск тестов быстрее.
#Independent (независимость) тестов - тоже очень важная штука. Имеется в виду независимость их как друг от друга, так и от окружения, в котором они запускаются. Если они зависимы друг от друга, то для запуска какого-то определенного теста придется запускать из скопом, что увеличивает время запуска (а это плохо). Если зависимы от окружения - значит где-то они не будут запускаться, либо где-то будут падать, чем начнут создавать проблемы либо вам, либо вашим коллегам. Так же нельзя забывать, что запуск тестов в многопоточной среде - это тоже запуск в определенном окружении. И если сами тестовые классы имеют stateful-природу (имеют состояние), то тесты получаются как зависимыми от этого состояния так и друг от друга. А потом еще и нарушают #Repeatable принцип, если начнут иногда падать при запуске с помощью многопоточного Runner’а, к которому многие проекты все равно приходят чтоб сделать запуск тестов быстрее.
First принципы - Repeatable
#Repeatable - повторяемость. Означает что ваш тест должен давать везде и всегда одинаковый результат - будь то ноутбук разработчика или суперкомпьютер в недрах компании. Смысл вполне должен быть очевиден - если тест иногда имеет false-negatives - т.е. иногда показывает что прошел, хотя должен был упасть - можно пропустить баги в продакшн. Когда падает изредка - приучает команду ретраить его просто до тех пор пока не пройдет, что не только раздражающе но и тратит время. Хотя само по себе “изредка падает” означает что что-то где-то пошло не так.
Есть у вас такие “изредка падающие” тесты?
#Repeatable - повторяемость. Означает что ваш тест должен давать везде и всегда одинаковый результат - будь то ноутбук разработчика или суперкомпьютер в недрах компании. Смысл вполне должен быть очевиден - если тест иногда имеет false-negatives - т.е. иногда показывает что прошел, хотя должен был упасть - можно пропустить баги в продакшн. Когда падает изредка - приучает команду ретраить его просто до тех пор пока не пройдет, что не только раздражающе но и тратит время. Хотя само по себе “изредка падает” означает что что-то где-то пошло не так.
Есть у вас такие “изредка падающие” тесты?
Что не так с таймаутами в тестах
JUnit-аннотации @Timeout и @Test(timeout) позволяют добавлять проверку скорости выполнения тестов, запуская тесты в отдельных потоках. Так вот, недавно мне доводилось бороться с их большим количеством в проекте. Так что же с ними не так? Любая возможная цель их добавления вступает в какой-либо конфликт с принципами хороших юнит тестов.
- Их иногда добавляют чтоб убедиться что тест работает быстрее, чем указанный возможный временной диапазон. Но ведь тесты запускаются в разных окружениях - на серверах и рабочих машинах, и иногда ресурсы могут быть заняты другими процессами, а значит некоторым тредам может выделиться недостаточно времени. Т.е. могут появиться false-fails. Таким образом наши тесты перестанут быть #repeatable и станут зависимыми от окружения - среда запуска не должна быть нагружена другими процессами.
- Иногда таймауты добавляют чтоб заставить мейнетейнеров проектов не делать тесты слишком длинными и чтоб держать общее время работы тестов под контролем. Проблема в том, что тесты с таймаутом создают новый тред для выполнения каждого теста, что само по себе дорогая операция, а значит общее время выполнения тестов растет. Не говоря уж о том, что нет четких критериев по длительности теста - в одних командах может быть 1000 тестов по 0,1 сек и это будет все равно в 2 раза дольше, чем в другой команде 100 тестов по 0.5 сек. Для изредка падающих по времени тесты разработчики либо увеличивают таймауты, что делает цель добавления таймаутов бесполезным делом, либо чаще ретраят запуски, что еще более увеличивает общее время прохождения тестов.
- Еще таймауты используют, чтоб убедиться что функционал работает оптимально. Тут проблема в том что при запуске юнит тестов мы не узнаем насколько оптимально написан код. Для этого нужны бенчмарки, но юнит тесты должны тестировать функциональность, а не время ее отработки. Для тестирования производительности есть нагрузочные и стресс тесты, и подходы к их написанию сильно отличаются от концепций юнит-тестирования. При попытке же интегрировать нагрузочный тест в юнит-тест появляются конфликты с принципом #selfvalidated.
Мои поиски о том как правильно использовать #timeout не привели к каким-то хорошим результатам, а опыт был только негативным. Поделитесь, пожалуйста, своим мнением.
JUnit-аннотации @Timeout и @Test(timeout) позволяют добавлять проверку скорости выполнения тестов, запуская тесты в отдельных потоках. Так вот, недавно мне доводилось бороться с их большим количеством в проекте. Так что же с ними не так? Любая возможная цель их добавления вступает в какой-либо конфликт с принципами хороших юнит тестов.
- Их иногда добавляют чтоб убедиться что тест работает быстрее, чем указанный возможный временной диапазон. Но ведь тесты запускаются в разных окружениях - на серверах и рабочих машинах, и иногда ресурсы могут быть заняты другими процессами, а значит некоторым тредам может выделиться недостаточно времени. Т.е. могут появиться false-fails. Таким образом наши тесты перестанут быть #repeatable и станут зависимыми от окружения - среда запуска не должна быть нагружена другими процессами.
- Иногда таймауты добавляют чтоб заставить мейнетейнеров проектов не делать тесты слишком длинными и чтоб держать общее время работы тестов под контролем. Проблема в том, что тесты с таймаутом создают новый тред для выполнения каждого теста, что само по себе дорогая операция, а значит общее время выполнения тестов растет. Не говоря уж о том, что нет четких критериев по длительности теста - в одних командах может быть 1000 тестов по 0,1 сек и это будет все равно в 2 раза дольше, чем в другой команде 100 тестов по 0.5 сек. Для изредка падающих по времени тесты разработчики либо увеличивают таймауты, что делает цель добавления таймаутов бесполезным делом, либо чаще ретраят запуски, что еще более увеличивает общее время прохождения тестов.
- Еще таймауты используют, чтоб убедиться что функционал работает оптимально. Тут проблема в том что при запуске юнит тестов мы не узнаем насколько оптимально написан код. Для этого нужны бенчмарки, но юнит тесты должны тестировать функциональность, а не время ее отработки. Для тестирования производительности есть нагрузочные и стресс тесты, и подходы к их написанию сильно отличаются от концепций юнит-тестирования. При попытке же интегрировать нагрузочный тест в юнит-тест появляются конфликты с принципом #selfvalidated.
Мои поиски о том как правильно использовать #timeout не привели к каким-то хорошим результатам, а опыт был только негативным. Поделитесь, пожалуйста, своим мнением.