Comments 23
Идиотский вопрос: а что, продолжать выполнение после сработавшего assert уже стало нормальным?
Именно что "даже не Exception". Сработавший ассерт — это ситуация "всё пропало, шеф!". Fatal Error. Что-то, чего мы не предусмотрели в принципе.
И, соответственно, чтобы имело смысл продолжать выполнение — надо вначале его починить, потому что всё, что происходит после него — происходит в сломанной системе.
Об этом и статья — не пользуйтесь ассертами в тестах, чтобы не попасть в такие ситуации.
А потом, скорее всего, перестанут писать ассерт там, где можно продолжать работу.
P.S. и ассерт может быть совсем не в коде теста, а в коде тестируемой функции или даже глубже. Так что остановиться придётся.
Если приложение Test Runner будет умирать на первом assert-e — программисты будут вынуждены починить этот ассерт для запуска остальных тестов. Нет проблемы.
Это если сразу понятно, какой из тестов обрушил остальные. Но когда внезапно падает 150 тестов, отыскивать тот единственный, из-за которого не работают все — удовольствие сомнительное. Оказывался в подобной ситуации, когда один из тестов начал выдавать StackOverflowException, чем обрушивал TestRunner.
Стоп-стоп. Если речь идёт об assert — всё сразу понятно. Который заасертился — тот и чинить.
Хотя, конечно, если всё плохо и какой-то тест ещё до заассертившегося сломал окружение и при этом не зафейлился… Но это совсем уж ад.
Я знал, знал, что их было 15: https://habr.com/post/420509/?reply_to=19009351#comment_19007515 :-D
Да, и какое решение в итоге приняли, чтобы повторно не наступить на грабли? Понятно, что запретить стандартный assert совершенно недостаточно (ну, в коде тестов ему не место, но он же может быть и в рабочем коде, а то и в используемых библиотеках).
Ловить assert-овский exception тоже и делать cleanup (затыкание конкретной дыры, не общее решение — плохо)? Перезагружать test runner по возникновению exceptions не из списка разрешённых? Повысить severity для тестов, проваленных по таким причинам, чтобы чинить их первыми? Ещё что-то?
Это если сразу понятно, какой из тестов обрушил остальные.
Ну так надо писать тесты на тесты. Они написали некий глючный функционал, который вызывает тесты, а потом ругают assert. Молодцы, чо.
В юнит-тестах продолжать выполнение других тестов помимо упавшего — всегда было нормально.
Маленькая тонкость: не упавшего (crashed), а проваленного (failed).
В случае крэша (а assert следует рассматривать именно как крэш) максимум — можно перезапустить процесс со следующего теста. Ну, это от фреймворка для тестов зависит, конечно. Но в любом случае — нужно переинициализировать окружение, а не полагаться на "зачистку", выполняемую заведомо сломанным кодом.
Маленькая тонкость: не упавшего (crashed), а проваленного (failed).
А как вы одно от другого отличаете? Ну вот по упавшему процессу можно, наверное, да. Но здесь же не тот случай.
а assert следует рассматривать именно как крэш
А почему, собственно?
Но в любом случае — нужно переинициализировать окружение, а не полагаться на "зачистку", выполняемую заведомо сломанным кодом.
Окружение? Какое такое окружение в юнит-тестах?
Отличаю просто: assert — только для фатальных ошибок. Рассматриваю ассерт как полезную разновидность крэша.
А для проверки на fail в тестах в известных мне тестовых фреймворках есть свои макросы (сорри, я из мира C++), не assert.
Окружение — ну, мы живём не в идеальном мире. Вон, у автора статьи транзакция незавершённая остаётся.
В идеальном мире мы бы каждый тест стартовали на новой машине с системой в исходном состоянии. Собственно, это можно сделать (виртуалка), но дорого (тесты слишком долго выполняться будут). Приходится обходиться тем, что есть.
Отличаю просто: assert — только для фатальных ошибок.
Это вы не тесты упавшие отличаете, а что в коде написано. А тесты падают с еще кучей разных вариантов, и так как же вы будете отличать crash от fail?
Более того, вот кто-то на входе в метод параметры assert x != null, а кто-то — guard.notnull(x), и почему это должно по-разному отражаться на тестах?
А для проверки на fail в тестах в известных мне тестовых фреймворках есть свои макросы
Ииии что? Типа, все, что не дошло до такого "макроса" (которые, кстати, принято называть assertions, и пишутся они вполне себе assert-что-то) — crash? Ну так у вас неимоверное количество тестов будет crashed, это непродуктивно.
Окружение — ну, мы живём не в идеальном мире. Вон, у автора статьи транзакция незавершённая остаётся.
Ну так у него и не очень юнит-тесты, прямо скажем.
По статье:
1. AssertionError, как и Exception — можно ловить по Throwable
2. Если имеется общий ресурс на весь тестовый сьют, который «дорого» поднимать для каждого теста заново — следует предусмотреть механизм гарантирующий очистку данного ресурса перед/после каждого тестового сценария.
Т.е. странно использовать именно джавовский assert, чье поведение еще зависит от конфиги рантайма…
Так что, неудивительно, что отхватили граблей.
Неочевидная проблема использования assert