Pull to refresh

Comments 57

В некоторых браузерах есть console.assert:
console.assert(2 + 2 === 4, "Something really bad happened to numbers!")
Assert'ы могут ещё выполнять роль документирования кода. Например декларировать ожидаемые диапазоны значений переменных более узкие, чем стандартные типы.
В .net во всех тестовых Framework используется Assert для проверки возвращаемых значений и пост условий.

Для самого же исходного кода программы в .net привычнее использовать Code.Contracts… Там есть и пост условия и предусловия и проверка в самом теле метода…

Есть конечно Debug.Assert, но я таких примеров мало видел.

А Вообще, была такая книжка- отладка приложений под Windows8 года так 2004 вроде, так вот там целая глава про эти Assert на С++
Я думаю речь о книжке Джона Роббинса Отладка приложений Microsoft .NET и Microsoft Windows. Очень стоящая книжка, must read. Microsoft её, кстати, раздавала в составе «боекомплекта разработчика» какое-то время тому назад.

А про Windows8 просто опечатка.
@ автору:
На мой взгляд у Вас немного путаница в терминологии.
1)
assert — это не просто условие во время исполнения программы, но прежде всего инструмент отладки. По этой причине в С++ в релизных билдах их уже нет, да и в других языках к примеру в Python assert
2)
То что Вы называете assert-ом проверки входных и выходных данных в статье на мой взгляд не правильно, лучше придерживаться мнения Страуструпа. Он называет это «пре и пост условия», а проверку значений в цикле «инвариантом» цикла.

Ну и стоит добавить что assert это способ сказать программеру о его ошибке, а не о том что там как-то звезды не так упали и фаза луны не та что ожидалась. Ассерт это «Эй чувак, тута не должно быть нуля, погляди код внимательней!!!»

ИМХО
п.1 снимаю, его вы описали другими словами почти в самом конце статьи. А ведь знание о том что assert не должен попадать в релизный билд очень важно!
я думаю релизный билд и ситуация с кастомером имеют одну природу, которая описана в следующем комментарии. Согласен с этим конечно же.
Может чуть улучшите статью, чтобы можно было ссылаться? )
Ассерт не должен срабатывать в релизном билде, потому что в этом билде не должно быть ошибок, от которых он защищает. Но если ошибка есть, то лучше пусть ассерт её поймает, чем она набедокурит сама. Ассерты из кода релизных билдов выключать ВРЕДНО.
1)
Еще раз, assert это не простое условие, а условие предназначенное для того чтобы быстро и мгновенно сказать программеру о том, что он не правильно понимает задачу. Т.е. инструмент поддержки продукта, когда к разработчику приходят и говорят о баге, тут он быстро достает дебажный билд и выясняет все что только можно.
То о чем вы говорите про «набедокурит» это runtime и logic ошибки. И это совсем из другой «оперы» ошибки.
2)
Посмотрим с другого угла зрения. assert — пишут с такой же целью что и unit-тесты. Вы же не включаете в продукт код unit-тестов? Лично я нет. Могу только иногда в отдельной папке ложить, если это библиотека.
Если у вас продукт хорошо обложен unit-тестами, то вы можете вообще не употреблять assert-ами.
3)
Как бы разработчик не старался весь код проверками невозможно обложить, ровно как и все и вся затестировать нельзя. Обложить все и вся проверками не получится, т.к. задачи и требования к продукту могут меняться. Поэтому код нужно обкладывать разумными проверками, который постарается правильно упасть, тем самым дать разработчику достаточную для поддержки информацию или упасть так, чтобы не привести к плачевным последствиям.

P.S.:
На сайте мелкософта можно закачать дебажное ядро Windows, попробуйте поработать с ним. В нем очень много assert-ов. Думаю вы поймете, когда надо, а когда не надо применять assert!
По ссылке указанной вами комент за «31 марта 2012, 16:42», а я же ответил на «31 марта 2012, 16:25». Не заметили? Вот последний как раз содержит весьма плохо-пахнущую практику «Ассерты из кода релизных билдов выключать ВРЕДНО». Перечитайте внимательней мой коммент.
там пример, подтверждающий моё утверждение, и отчасти опровергающий Ваше.
Вы что курите?

В одном комментарии Вы пишете:
>>31 марта 2012, 16:25
>>Ассерты из кода релизных билдов выключать ВРЕДНО

А в другом:
>>31 марта 2012, 16:42
>>Считаете, кастомеру больше понравится неопределённое поведение? Особенно он будет счастлив, когда Вы не сможете его локализовать и исправить.

То что кастомеру больше понравиться… ежу понятно и это даже не обсуждается именно за тем и работает программер, что меньше парить мозг кастомеру. С этим ни кто не спорит!

Ваше слова про ассерт, что их нужно оставлять в релизе говорят о том что Вы не совсем понимаете смысл и цель существования assert!!! Вы путаете понятие assert c проверкjq, которая проверяет что-то в коде и в случае чего правильно валит программу либо еще что-то!
Обратите внимание на перевод слова «assert» с англ. на русский. «утверждаю». Это своего рода документация на исходный код потомкам «Утверждаю что в этом месте не должно быть....». Это совсем другое и отличное от привычных нам конструкций в коде на вроде: «Если вдруг значение переменной икс больше… тогда покажем багрепорт».
Приведу одну из ситуаций, где нужно использовать assert. К примеру Вы только вчера вышли на новую работу и Вам дали проект с большим количеством строк. В проекте Вы еще ничего не знаете и только еще приступили разбираться. Перед Вами поставлена задача за 1-2 дня поправить багу и выкатить новый update. В ходе работы с кодом и его отладкой вы вдруг находите багу. Допустим она в неком хитро-вывернутом цикле с некисло написанным коде, который не мешало бы отрефакторить. Цикл помещен в функцию которая очень часто вызывается и тем самым влияет на эффективность работы приложения, к примеру подбор пароля.

Каковы Ваши действия?

На мой взгляд такие варианты:
1) Просто внести изменения в код, тем самым поправить багу. Выкатить обновление. Отрапортовать о выполнении задачи шефу.
2) Вы непросто фиксаете код, но и попутно пишите unit-тест, который в будущем позволит Вам или Вашим коллегам проверить этот цикл на корректность работы.
3) Вы фиксаете код и пишите assert.

Выводы:
1) Если идти по варианту №1 то в будущем вы можете забыть работу этого цикла и снова придется вникать
2) Если же идти по варианту №2, то Вы можете не уложиться в отведенные на задачу 1-2 дня!!!
3) Самое наилучшее решение, этим самым вы говорите самому себе в будущем и своим коллегам «Утверждаю что в этом коде переменная1 не должна быть больше чем переменная2», ну или еще чтото.

Если вы пойдете по варианту №3, то выкатив на рынок обновление ПО Вы никак не ухудшите его работу по скорости, т.к. лишней проверки assert нету в релизе.
Однако при этом в случае проблем Вы имеет возможность скомпиллировать дебажную версию и получить так нужные вам assert-ы, которые помогают вам понять докопаться до проблемы
UFO just landed and posted this here
На мой взгляд, статья не полностью покрывает весь спектр того, что стоит использовать при программировании в энтерпрайзе. Вот представим, что у нас продукт, который стоит у сотни тысяч кастомеров и одна ошибка будет дорого стоить. Что тогда? Согласны ли вы, чтобы ваш любимый софт иногда падал с ассертом в каком-то специфическом сценарии. Да, конечно, с точки зрения прогаммиста все пучково — есть баг, ассерт его поймал вовремя, вот колстек, вперед. Попробуйте это объяснить кастомеру. А кастомер этот какой-нибудь крупный банк или страховщик. Что он скажет? Скажет: вот ассерт у вас тут сработал, вот вам колстек, коредамп, молодцы мужики, что не оставили неопределенное поведение. Да? Сомневаюсь. С точки зрения программиста ассерты — это добро, с точки зрения кастомера — зло. Поэтому правильный подход — использование исключений и тотальный контроль и обработка ошибок. Любой ассерт должен быть продублирован обработкой ошибок. А самые классные ассерты — это те, которые кидают исключение при срабатывании. Но для этого необходимо транзакционное поведение на любом уровне. Вот этого я, к сожалению, не увидел в статье.
Не спорю, но покрыть всё не так уж и сложно. Насчёт ситуации с кастомером также имею схожее мнение.
Считаете, кастомеру больше понравится неопределённое поведение? Особенно он будет счастлив, когда Вы не сможете его локализовать и исправить.
Считаю, что кастомеру ничего не понравится. Ему локализация бага неинтересна, он уже теряет деньги на этом баге. Ему интересно, когда он перестанет их терять. Поэтому тут все несколько сложнее. По хорошему, ассерты должны быть продублированы правильным «еррор-хендлингом», т.е. обработкой ошибок. Именно так рекомендуют делать для серьезных программ.
Тут два утверждения, которые противоречат друг другу: «показ assert'ов — это плохо» и «делайте assert'ы на исключениях с транзакционным поведением и обработкой ошибок» (обработка = показ пользователю и восстановление работы?).

Имхо, перехватив исключение, нужно всё-таки показать пользователю ошибку и не пытаться её маскировать. Неопределённое поведение всем выйдет дороже, а протестировать всё — утопия. И да, исключения и транзакционность — это правильно, +1.
Извините, но накачать полный винчестер контента с гей-порносайта — это вполне определенное поведение.
Ассерты — это способ описания/документации невозможной ситуации. Невозможной, при условии, что написанный код будет использоваться правильно. То есть ассерты — они для программистов, для более раннего нахождения грубых ошибок в коде, для того, чтобы обратить внимание других программистов на обязательные предусловия/постусловия. В идеале все должно быть так, чтобы для конечного пользователя поведение было абсолютно одинаковое, что с включенными ассертами, что с выключенными. А обработка ошибок, диагностика, core dumps, etc в готовом продукте должны выполняться другими средствами.
Нет, если она может его накачать или не накачать в зависимости от положения звезд.
Как раз недавно с товарищем обсуждали использовать ли assert'ы.

1. Мне кажется в этом примере:
…
is_success = acquire_mutex(mtx);
assert(is_success);

// Теперь данные защищены мютексом даже при отключенных assert'ах.
process_data(data_protected_by_mtx);
…

хоть и не исчезнет попытка взятия мьютекса, но проверка на успешность вполне может исчезнуть вместе с assert'ом. Надо как-то проверять, бросать исключения.

2. Вариант для JS какой-то бессмысленный — зачем делать функцию кидания одинаковых исключений, когда лучше в каждом конкретном случае кидать что-то своё, чтоб видеть что именно отвалилось без копания в стектрейсе?
Алгоритм, вылавливания ошибки:
1) Изучаешь функцию, в которой произошел сбой, на предмет ошибок.
2) Если ошибка не обнаружена, то находишь все места вызова функции, в которой произошел сбой. Для каждого найденого места вызова функции инспектируешь код, который в последний раз модифицировал данные, на которых произошел сбой.
3) Если источник ошибки не найден, рекурсивно просматриваешь места вызова функций, из которых была вызвана наша функция.
4) Если задолбало инспектировать код функций, то добавляешь недостающие assert'ы и/или trace-сообщения, которые бы позволили более точно локализировать ошибку, пересобираешь программу и предлагаешь ее пользователю :)
5) Если это не помогло, то добавляешь в программу возможность автоматической отправки крэшдампа на наш сервер, и отсылаешь пересобранную программу пользователю :)
Грамотно расставленные assert'ы, находящиеся в районе инспектируемых функций и проверяющие сбойные данные, позволяют эффективно отсечь из рассмотрения функции, находящиеся выше по стэку. В идеале баг находится на либо на первом, либо на втором шаге.
Т.е. мы заменяем полноценные тесты дополнительными проверками в коде (которые или выкидывают исключение, или завершают программу с ошибкой). Не легче ли снабдить исходные коды папочкой tests и и сключать эту папку из финальной сборки?
Нет, ассерт это не тест.
Тест проверяет результат программы при заданных входных параметрах:
(2 * 2 == 4)
Ассерт проверяет инварианты хода выполнения программы и никак не зависит от входных данных:
(2 * 3 == 3 * 2)
Ассерты и тесты скорее взаимодополняющие инструменты в этом применении.

Тестами мы проверяем, что получаем правильный результат при корректных параметрах, а ассертами, что всегда (по крайней мере во время выполнения тестов и просто запусков дев-билдов) получаем корректные параметры.
извини, мимо плюса промахнулся.
> Когда нельзя использовать assert'ы?
В деструкторах C++ объектов.
Вылет исключения при размотке стека (в деструкторе автоматического объекта) из-за срабатывания другого исключения (например, проверка как-то условия) приводит к аварийному завершению изделия вообще без диагностики.
Assert-ы не всегда бросают исключения. Например, в Qt Q_ASSERT останавливает отладчик и выводит ошибку в консоль.
Q_ASSERT вызывает qFatal(), а qFatal() может и программу завершить если не установить свой message handler.
Это понятно, но в деструкторах его без проблем можно использовать, диагностику это не затрудняет.
Кстати, да — полезная в реализации (своего) assert'а функция: ::DebugBreak() — иммитирует break-point и останавливает отладчик в месте вызова. На других платформах, думаю, есть что-то похожее.
Пусть меня заминусуют, но эта статья — восторженно-рекламный бред дилетанта. Чушь, заблуждения или ошибки практически в каждом абзаце.
Восторженно-рекламный бред дилетанта — это писать комментарии не аргументируя своё мнение.
другие, не столь категоричные комменты, часть проблем поста уже обозначили. Вам мало? доберусь до ББ — выпишу ещё.

а к «писать комментарии не аргументируя своё мнение» ни один из скопированных Вами эпитетов не подходит.
Категорически с Вами не согласен с утверждением: «Проверки на этапе компиляции не так важны»
Только не для компилируемого языка со строгой типизацией, где можно многие проверки переложить на компилятор.
Когда программу с ошибками вообще нельзя скомпилировать — это самая лучшая проверка, и самая важная.

А насчет асертов, то как их использовать вместе с юнит тестами?
Я когда вижу асерт в функции, то это 100% гарантия того, что это условие не проверяется юнит тестами.

При нарушении входных параметров, к примеру нулевой указатель, можно и поставить асерт, но тогда предпочитаю чтобы программа упала в релизе, иначе, если функция вернет к примеру false, то я не смогу написать автоматический тест для того, чтобы проверить что все в порядке при такой ситуации, опять же из-за асерта.

Мои высказывания были по поводу асертов в C++.
А насчет асертов, то как их использовать вместе с юнит тестами?
Я когда вижу асерт в функции, то это 100% гарантия того, что это условие не проверяется юнит тестами.


Допустим есть условие, что функция (сложная) не должна возвращать NULL. Как вы будете проверять это юнит-тестами? В лучшем случае вы проверите несколько комбинаций параметров и убедитесь, что они возвращают ожидаемые значения. Но это не даёт гарантии, что NULL не вернётся при других комбинациях. Ассерт же гарантирует (если они включены), что функция не вернёт NULL ни при каких условиях, программа просто вылетит. Тесты, кстати, тоже не пройдут.
Assert, как правило, используется только в отладочной версии и ничего не гарантирует в релизной.
А значит он бесполезен, если функция будет возвращать NULL у пользователя.
Лучше вместо асерта использовать ислключения, если необходима гарантированная проверка.
(Либо не отключать асерты в релизе, но тогда это уже не асерты, а исключения)

Асерты хорошо использовать для проверки ошибок программирования, так сказать статических ошибок, на которые не влияют внешние факторы. Например корректность параметров при вызове функции и т. п.

Но для ошибок времени выполнения, которые могут быть вызваны внешними факторами, например не открылся файл, асерты использовать неправильно. Для этой цели есть другие методы отладки, например логирование.

Несколько ошибок я ловил ассертами во время функциональных тестов, именно ошибок программирования — где-то опечатался, где-то ещё что-то в этом роде. И не думал их использовать для ловли ожидаемых ошибок, таких как валидация пользовательского ввода или взаимодействия с внешними системами. Вот дескриптор уже открытого файла, передаваемый скажем в функцию чтения, внтури неё проверить можно ассертом. Он не должен быть NULL по логике приложения никогда. Если не открылся, то исключение должно выскочить раньше вызова функции, но вдруг где-то что-то упустил…
Ассерты в релизе — всё равно ассерты, потому что ассерты — это исключения, касающиеся нарушений предусмотренных их автором соглашений относительно каких-либо значений переменных в соответствующих местах алгоритмов. И не более того.
Asset выглядит «костылём», что ли, если используется только проверки корректности входных параметров и инваринта класса. Для этого есть контрактное программирование (Design By Contract).
дык асерты и есть один из инструментов реализации контрактного программирования, разве нет?
Контракты в синтаксисе по сути лишь сахар для ассертов.
Хочу заметить, что использовать ассерты для проверки входных данных public-функции — ошибка. Функция должна проверять аргументы в любом случае и корректно реагировать (либо бросать исключение, либо возвращать специальное значение, сигнализирующее о недопустимых значениях аргументов). Assert для проверки входных данных можно использовать только в private-методах (в том случае, когда вы точно знаете, с какими аргументами вызываете свой метод). Потому что срабатывание assert рассматривается как ошибка В КОДЕ ФУНКЦИИ а не в вызывающем коде, и если вы оставите public функцию с ассертом на входные данные, то потом будете лезть в функцию и искать ошибку там, где её нет, вместо того, чтобы проанализировать ArgumentException.
срабатывание assert рассматривается как ошибка В КОДЕ ФУНКЦИИ а не в вызывающем коде

Проверка assert-ом входящих параметров к такому определению не относится.
А ещё можно писать так
assert instance != null: «Instance is null!»;
без скобок и со своим сообщением
Не особо наглядно. Если говорить про С, то лучше применять классическую конструкцию:
assert( instance != null && "Instance is null!" );
Sign up to leave a comment.

Articles