Как стать автором
Обновить

Комментарии 23

Идиотский вопрос: а что, продолжать выполнение после сработавшего assert уже стало нормальным?

ну если сработавший assert — это даже не Exception то почему бы не продолжить

Именно что "даже не Exception". Сработавший ассерт — это ситуация "всё пропало, шеф!". Fatal Error. Что-то, чего мы не предусмотрели в принципе.
И, соответственно, чтобы имело смысл продолжать выполнение — надо вначале его починить, потому что всё, что происходит после него — происходит в сломанной системе.

Если приложение Test Runner будет умирать на первом assert-e, как и положено для ассертов, а тесты будут проверять пост-условия ассертами, то первый проваленный тест остановит тестирование и не позволит увидеть картину по всем тестам.

Об этом и статья — не пользуйтесь ассертами в тестах, чтобы не попасть в такие ситуации.
Если приложение Test Runner будет умирать на первом assert-e — программисты будут вынуждены починить этот ассерт для запуска остальных тестов. Нет проблемы.
А потом, скорее всего, перестанут писать ассерт там, где можно продолжать работу.

P.S. и ассерт может быть совсем не в коде теста, а в коде тестируемой функции или даже глубже. Так что остановиться придётся.
Если приложение Test Runner будет умирать на первом assert-e — программисты будут вынуждены починить этот ассерт для запуска остальных тестов. Нет проблемы.


Это если сразу понятно, какой из тестов обрушил остальные. Но когда внезапно падает 150 тестов, отыскивать тот единственный, из-за которого не работают все — удовольствие сомнительное. Оказывался в подобной ситуации, когда один из тестов начал выдавать StackOverflowException, чем обрушивал TestRunner.

Стоп-стоп. Если речь идёт об assert — всё сразу понятно. Который заасертился — тот и чинить.
Хотя, конечно, если всё плохо и какой-то тест ещё до заассертившегося сломал окружение и при этом не зафейлился… Но это совсем уж ад.

Может, я что-то не так понял, но у меня сложилось впечатление, что внезапно 150+ тестов стали красными, при это ошибка только в одном, а остальные не смогли выполниться из-за этой ошибки. И да, исправить нужно только один тест, но сначала его надо найти из 150+ красных. Непродуктивная трата времени.

Ну вот если бы всё падало на assert — была бы диагностика типа "10 красных тестов, тест XXX вообще упал, невозможно продолжать". И автор починил бы XXX срочно, прогнал бы тесты заново и получил, допустим, 15 красных (10 до XXX и 5 после). И уже не спеша "озеленял" бы остаток.

Один тест зафейлил окружение (оставил незавершенной транзакцию по единственному коннекту), при этом на нем был AssertionError, другие 150 упали из-за невозможности получить коннект, при этом часть стала красными, часть желтыми. Ситуация усугблялась тем, что задача в целом стояла починить тесты на сборке, то есть кроме 1 упавшего ассерта лежало еще порядка 15 тестов не связанных с этой ошибкой.

Я знал, знал, что их было 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, это непродуктивно.


Окружение — ну, мы живём не в идеальном мире. Вон, у автора статьи транзакция незавершённая остаётся.

Ну так у него и не очень юнит-тесты, прямо скажем.

Не очень понятно — почему неочевидная проблема?
Ожидал прочитать откровения по особенностям внутренней реализации инструкции assert на уровне байт-кода.
По статье:
1. AssertionError, как и Exception — можно ловить по Throwable
2. Если имеется общий ресурс на весь тестовый сьют, который «дорого» поднимать для каждого теста заново — следует предусмотреть механизм гарантирующий очистку данного ресурса перед/после каждого тестового сценария.
НЛО прилетело и опубликовало эту надпись здесь
А все эти Assertions.assertEquals(), которые бросают Exception, для кого писали?
Т.е. странно использовать именно джавовский assert, чье поведение еще зависит от конфиги рантайма…
Так что, неудивительно, что отхватили граблей.
Возможно, программист, который писал assert, использовал его по прямому назначению — проверять инварианты в runtime, вообще не думал о тестах (Assertions у него нет в зависимостях класса). Но при подключении тестов обнаружилась такая особенность.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории