...
var combo = view.ActiveEditor as ComboBoxEdit;
Debug.Assert(combo != null, "Column editor supposed to be a ComboBoxEdit");
if (combo == null)
return;
...
Это совсем @овнокод или нет?
На всякий случай: это не в модели и не в логике, т.е. ничего и не покорруптится, и по-тихому для пользователя не сделается / не несделаеся.
Мотив проверять: в реализации "view" кто-то что-то изменить (или оно уже сейчас не всегда работает так, как я предполагаю).
Мотив в релизе не кидать эксэпшн: у пользователя просто не будет работать один контрол но все остальное будет доступно и ничего не потеряется
Y>...
Y>var combo = view.ActiveEditor as ComboBoxEdit;
Y>Debug.Assert(combo != null, "Column editor supposed to be a ComboBoxEdit");
Y>if (combo == null)
Y> return;
Y>...
Y>
Y>Мотив проверять: в реализации "view" кто-то что-то изменить (или оно уже сейчас не всегда работает так, как я предполагаю). Y>Мотив в релизе не кидать эксэпшн: у пользователя просто не будет работать один контрол но все остальное будет доступно и ничего не потеряется
Для данных мотивов вполне приемлемо, на мой взгляд.
Y>А если добавить логи и репорты в релизе?
Почему нет. Завести какой-нибудь лог для ошибок и писать туда подобные ситуации.
Y>...
Y>var combo = view.ActiveEditor as ComboBoxEdit;
Y>Debug.Assert(combo != null, "Column editor supposed to be a ComboBoxEdit");
Y>if (combo == null)
Y> return;
Y>...
Y>
Y>Это совсем @овнокод или нет?
Любые проверки на null указывает на то, что объект имеет невалидное состояние и по сути создан быть не должен был бы (RAII, ага). тут у тебя либо ожиаемое поведение, н-р ты сделал это намеренно и тут должна быть логика какая-то, либо ты перестраховываешься, тогда тут надо просто поставить assert, спокойно валится и отлавливать это на этапе дебага. Вопрос почему объект тут стал таким (имхо, такого быть не должно, это assert) надо адресовать к тому, кто его создаёт.
Здравствуйте, ylem, Вы писали:
Y>Мотив проверять: в реализации "view" кто-то что-то изменить (или оно уже сейчас не всегда работает так, как я предполагаю). Y>Мотив в релизе не кидать эксэпшн: у пользователя просто не будет работать один контрол но все остальное будет доступно и ничего не потеряется
Мы для таких вещей пишем
var combo = view.ActiveEditor as ComboBoxEdit;
DebugCode.BugIf(combo == null, "Bug: combo is not ComboBoxEdit");
...
Т.е. для отладочного кода — падаем с понятным сообщением, для релиза — никаких проверок и (скорее всего) падаем с NRE.
Это в типовом случае "код из нагруженного участка, покрыт тестами, ошибки быть в принципе не может, для подстраховки — вот тебе ассерт".
Если ситуация другая, то можно или оставить ассерт и в релизных сборках, или (ниже) проверять combo на null и действовать по обстоятельствам.
Меня подобный вариант не очень привлекает, т.к. такие костыли явно намекают на косяк в дизайне приложения. Лучше написать нормально и не оставлять грабли на потом.
Здравствуйте, Sinix, Вы писали:
S>Мы для таких вещей пишем S>
S> var combo = view.ActiveEditor as ComboBoxEdit;
S> DebugCode.BugIf(combo == null, "Bug: combo is not ComboBoxEdit");
S> ...
S>
И чем это лучше чем банальный:
var combo = (ComboBoxEdit)view.ActiveEditor;
...
S>Т.е. для отладочного кода — падаем с понятным сообщением, для релиза — никаких проверок и (скорее всего) падаем с NRE.
Фи. Или падаем, но не сразу — если combo используется где-то в другом участе кода чуть позже — счастливой отладки.
S>Это в типовом случае "код из нагруженного участка, покрыт тестами, ошибки быть в принципе не может, для подстраховки — вот тебе ассерт".
ассерты фтопку.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ·, Вы писали:
·>И чем это лучше чем банальный:
Как минимум тем, что отлавливает ещё и ActiveEditor == null.
Если серьёзно, то конкретно в этом примере смысла расставлять ассерты нет, проще код поправить. А вот подстелить соломку в местах, где закладываешься на текушее поведение, аля
protected override void RunCore(... List<Measurement> resultMeasurements)
{
DebugCode.BugIf(resultMeasurements.Count>0, "resultMeasurements not empty.");
DebugCode.BugIf(resultMeasurements.Capacity < iterationCount, "resultMeasurements capacity not set.");
иногда спасает. Буквально вчера этот ассерт словил баг с передачей не того списка в метод.
S>>Т.е. для отладочного кода — падаем с понятным сообщением, для релиза — никаких проверок и (скорее всего) падаем с NRE. ·>Фи. Или падаем, но не сразу — если combo используется где-то в другом участе кода чуть позже — счастливой отладки.
Ну а что мешает оставить ассерт и для релизных сборок? Я ж не зря написал условие, когда достаточно только отладочного ассерта:
код из нагруженного участка, покрыт тестами, ошибки быть в принципе не может, для подстраховки — вот тебе ассерт
·>ассерты фтопку.
А аргументы какие-нить будут, кроме как "мне не нравится"? А то типовая ситуация с проектами "ассерты не нужны" выглядит вот так: тесты зелёные, CI настроен, покрытие норм, форкаем, ставим ассерты — и оппа баг раз, оппа баг два, упс — недоработка три и тыды и тыпы.
Причём сам код — отличный и с сценариями авторов проблем нет вообще. Проблема с непредусмотренными сценариями. Для них тестов нет, проверок нет и распутывание ошибок превращается в натуральный квест типа такого.
Ассерты подобные косяки ловят на раз-два, т.к. они работают всегда, а не проверяются только в одном тесте из нескольких тысяч.
Здравствуйте, Sinix, Вы писали:
S>·>И чем это лучше чем банальный: S>Как минимум тем, что отлавливает ещё и ActiveEditor == null.
Тогда лучше явная проверка на null. А в "view.ActiveEditor as ComboBoxEdit" непонятно — толи тупо null, толи тип не тот. Смешивается несмешиваемое.
S>Если серьёзно, то конкретно в этом примере смысла расставлять ассерты нет, проще код поправить. А вот подстелить соломку в местах, где закладываешься на текушее поведение, аля S>
S>иногда спасает. Буквально вчера этот ассерт словил баг с передачей не того списка в метод.
А просто if(resultMeasurements.Count>0) thrown Exception("resultMeasurements not empty.") чем хуже?
S>·>Фи. Или падаем, но не сразу — если combo используется где-то в другом участе кода чуть позже — счастливой отладки. S>Ну а что мешает оставить ассерт и для релизных сборок? Я ж не зря написал условие, когда достаточно только отладочного ассерта:
Если оставлять ассерт для релизных сборок — зачем тогда ассерт, а не простой код?
S>
S>код из нагруженного участка, покрыт тестами, ошибки быть в принципе не может, для подстраховки — вот тебе ассерт
Если "ошибки быть в принципе не может" — тогда и проверка не нужна. А раз страхуешься, значит неверно что "ошибки быть в принципе не может". Ты уж определись.
S>·>ассерты фтопку. S>А аргументы какие-нить будут, кроме как "мне не нравится"? А то типовая ситуация с проектами "ассерты не нужны" выглядит вот так: тесты зелёные, CI настроен, покрытие норм, форкаем, ставим ассерты — и оппа баг раз, оппа баг два, упс — недоработка три и тыды и тыпы.
Ассерты говорят от дыре в дизайне, которая не позволяет написать простой юнит-тест для того же условия.
S>Причём сам код — отличный и с сценариями авторов проблем нет вообще. Проблема с непредусмотренными сценариями. Для них тестов нет, проверок нет и распутывание ошибок превращается в натуральный квест типа такого.
Ну раз ассерт догадались написать, значит и ютест можно было написать.
S>Ассерты подобные косяки ловят на раз-два, т.к. они работают всегда, а не проверяются только в одном тесте из нескольких тысяч.
Если ассерты работают всегда, то почему это ассерты? Под ассертами подразумевается код отключаемый в релизе. А если их не отключают — то значит они используются не по назначению.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ·, Вы писали:
·>Тогда лучше явная проверка на null. А в "view.ActiveEditor as ComboBoxEdit" непонятно — толи тупо null, толи тип не тот. Смешивается несмешиваемое.
тож вариант.
·>Если оставлять ассерт для релизных сборок — зачем тогда ассерт, а не простой код?
Чтобы не изобретать велосипед, если часть проверок должна быть только в отладочных сборках. К примеру, проверка на "все элементы в коллекции — не null". Смысла в релиз её пихать обычно нет, при отладке — ок.
·>Если "ошибки быть в принципе не может" — тогда и проверка не нужна. А раз страхуешься, значит неверно что "ошибки быть в принципе не может". Ты уж определись.
Ошибки "в принципе быть не может" на текущей codebase. Как, к примеру, ловить некорректные значения, возвращённые сторонним кодом? Писать на каждый случай отдельный тест — не вариант, т.к. надо точно воспроизвести интересующий нас сценарий, а не просто проверить "ну вот сейчас нормальное значение пришло, значит всё ок". С воспроизведением получим кучу лишнего кода, который ещё и синхронизировать с тем, что в основном проекте лежит.
·>Ассерты говорят от дыре в дизайне, которая не позволяет написать простой юнит-тест для того же условия. ·>Ну раз ассерт догадались написать, значит и ютест можно было написать.
Ну эт только в мелких проектах работает. В боль-менее крупных сплошь и рядом нет нужных тестов. Написать их не проблема, но по факту никто ими не озаботился — авторы проекта сценарий не предусмотрели или он для них не актуален. Или с их точки зрения поведение правильное и ошибка из-за некорректной настройки — это проблемы клиента. Чего в подобных ситуациях делать?
Или как в нашем случае — чтоб воспроизвести баг, нужно продублировать весь сценарий, т.к. итоговая ошибка вызвана мелкими логическими несостыковками в 3 независимых кусках кода. Ок, воспроизвели, починили, завели тест, всё зелёное. Какие гарантии, что эта же ошибка не воспроизведётся при другой комбинации условий и как этот момент вообще отловить?
Ну, т.е. два варианта: или ставим ассерт, или рассказываем про "ассерты не нужны" и "да всё ок, это у вас дыра в дизайне".
·>Если ассерты работают всегда, то почему это ассерты? Под ассертами подразумевается код отключаемый в релизе. А если их не отключают — то значит они используются не по назначению.
А какой смысл придумывать отдельный термин для "что-то типа ассертов, только в релизе работает"?
Здравствуйте, ylem, Вы писали:
Y>Это совсем @овнокод или нет? Y>Мотив в релизе не кидать эксэпшн: у пользователя просто не будет работать один контрол но все остальное будет доступно и ничего не потеряется
Зависит от значимости контрола, конечно. Есть контролы, если они отсутствуют, то нельзя продолжать работу. В этом случае — понятный эксепшн.
Если контрол необязательный (например,пользователю будет недоступна часть некритичного функционала), в этом случае бить весь вью конечно не резон.
Y>А если добавить логи и репорты в релизе?
Я последнее время прихожу к тому (и реализовываю), что в таких случаях, когда отсутствие чего-то не особо важно или зависит от внешних факторов, но работу приложения прерывать не стоит, делаю систему событий. Т.е. информировать нужно обязательно, проблема должна быть как-то показана. Будет ли это на последнем этапе провайдер обработки сообщений в виде лога, либо запись в базу, либо отображение на экране, зависит от приложения, но информирование должно быть.
Здравствуйте, Sinix, Вы писали:
S>·>Если оставлять ассерт для релизных сборок — зачем тогда ассерт, а не простой код? S>Чтобы не изобретать велосипед, если часть проверок должна быть только в отладочных сборках. К примеру, проверка на "все элементы в коллекции — не null". Смысла в релиз её пихать обычно нет, при отладке — ок.
Ок, в качестве обхода косяка дизайна, хотя и тут лучше подойдёт logger. В нормальной ситуации — использовать коллекцию, которая не допускает null-элементы (кидает NRE например, при попытке добавить таковой) — чтобы обнаруживать косяк в момент его возникновения, а не потом когда-нибудь, если повезёт.
S>·>Если "ошибки быть в принципе не может" — тогда и проверка не нужна. А раз страхуешься, значит неверно что "ошибки быть в принципе не может". Ты уж определись. S>Ошибки "в принципе быть не может" на текущей codebase. Как, к примеру, ловить некорректные значения, возвращённые сторонним кодом?
Корректно обрабатывать — кидать исключение, например.
S>Писать на каждый случай отдельный тест — не вариант, т.к. надо точно воспроизвести интересующий нас сценарий, а не просто проверить "ну вот сейчас нормальное значение пришло, значит всё ок". С воспроизведением получим кучу лишнего кода, который ещё и синхронизировать с тем, что в основном проекте лежит.
Почему не вариант? Это говорит о том, что ассерт написать проще, чем ютест. А это говорит о том, что ютесты пишутся сложно. А это говорит о том, что код не самого лучшего качества.
S>·>Ассерты говорят от дыре в дизайне, которая не позволяет написать простой юнит-тест для того же условия. S>·>Ну раз ассерт догадались написать, значит и ютест можно было написать. S>Ну эт только в мелких проектах работает. В боль-менее крупных сплошь и рядом нет нужных тестов. Написать их не проблема, но по факту никто ими не озаботился — авторы проекта сценарий не предусмотрели или он для них не актуален. Или с их точки зрения поведение правильное и ошибка из-за некорректной настройки — это проблемы клиента. Чего в подобных ситуациях делать?
Почему авторы таки озаботились ассертом, но ютестом озаботиться не смогли?
S>Или как в нашем случае — чтоб воспроизвести баг, нужно продублировать весь сценарий, т.к. итоговая ошибка вызвана мелкими логическими несостыковками в 3 независимых кусках кода. Ок, воспроизвели, починили, завели тест, всё зелёное. Какие гарантии, что эта же ошибка не воспроизведётся при другой комбинации условий и как этот момент вообще отловить?
Я не понял из описания issue взаимосвязь с ассертами. Можно поподробнее?
S>Ну, т.е. два варианта: или ставим ассерт, или рассказываем про "ассерты не нужны" и "да всё ок, это у вас дыра в дизайне".
Третий вариант — пишем тест.
S>·>Если ассерты работают всегда, то почему это ассерты? Под ассертами подразумевается код отключаемый в релизе. А если их не отключают — то значит они используются не по назначению. S>А какой смысл придумывать отдельный термин для "что-то типа ассертов, только в релизе работает"?
"displays a message box" — гы-гы. В духе майкрософт. Программисткая лажа лезущая прямо в морду несчастных юзеров. Особенно замечательно подойдёт для "код из нагруженного участка". Вот подумай, сидит бухгалтерша, тыкает кнопки и вдруг у неё вылазит "Column editor supposed to be a ComboBoxEdit". И чё?!
И как этот код будет работать, если вдруг попадёт на какой-нибудь server side?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ·, Вы писали:
·>В нормальной ситуации — использовать коллекцию, которая не допускает null-элементы (кидает NRE например, при попытке добавить таковой) — чтобы обнаруживать косяк в момент его возникновения, а не потом когда-нибудь, если повезёт.
Как по мне, подход в стиле "задача не решается легко == косяк в дизайне" годится только для сферического дизайна в вакууме.
В реальной жизни
* ты ограничен существующими коллекциями и интерфейсами. Никто не будет использовать API, для которого нужно завернуть данные в свой особый тип коллекции.
* ты ограничен производительностью. Пришёл, скажем, массив и требование — без аллокаций. Выкручивайся как хочешь.
* ты ограничен ресурсами. Ну ок, задизайнили мы коллекцию, которая не принимает null-ы. И тут оппа — другая задача не принимает коллекций из пустых строк, третья — отрицательных индексов, четвёртая — timespan больше часа и вдруг выясняется, что все четыре — это атрибуты одной сущности (скажем записи из semlog-а) и все приседания с волшебной коллекцией идут лесом. Никто не даст время на такие эксперименты, задачу надо сделать и переходить к следующим.
Что печально, даже появление в шарпе аналога Units of Measure от F# делу не поможет. Куча существующего кода с типами-примитивами никуда не денется, увы.
Так вот, когда пишут код без оглядки на подобные ограничения, вечно получается абсолютно неадаптированное к реальной жизни решение. Не надо так делать
S>>Ошибки "в принципе быть не может" на текущей codebase. Как, к примеру, ловить некорректные значения, возвращённые сторонним кодом? ·>Корректно обрабатывать — кидать исключение, например.
Ну так ассерты именно это и делают. Вся разница — они позволяют переиспользовать проверки без копипасты. Ну, т.е. вернулись к началу: ассерты — фу-фу-фу, просто исключения — ок. Серьёзно?
·>Почему не вариант? Это говорит о том, что ассерт написать проще, чем ютест. А это говорит о том, что ютесты пишутся сложно. А это говорит о том, что код не самого лучшего качества.
См выше про сферический дизайн. Довод "это у вас код неправильный" — первейший признак сабжа.
Если коротко, то ассерты ортогональны тестам. Тесты документируют и проверяют контракт отдельных кусков кода. Ассерты обеспечивают соблюдение инвариантов для _всех_ реальных сценариев использования. Каждый инструмент решает свою задачу, смысл тут спорить-то?
S>>Или как в нашем случае — чтоб воспроизвести баг, нужно продублировать весь сценарий, т.к. итоговая ошибка вызвана мелкими логическими несостыковками в 3 независимых кусках кода. Ок, воспроизвели, починили, завели тест, всё зелёное. Какие гарантии, что эта же ошибка не воспроизведётся при другой комбинации условий и как этот момент вообще отловить? ·>Я не понял из описания issue взаимосвязь с ассертами. Можно поподробнее?
Очень просто — наш проект упал с ассертом "строка в .csv содержит неэкранированный разделитель", текущая культура Central Kurdish, разделитель списка — ؛. Заметь, что для этого нам не пришлось писать отдельный тест для csv, перебирать все культуры (эта, к примеру, появилась в восьмёрке, пока CI-сервер был на 2012-м — фиг бы поймали), просто прогнали интеграционный тест и он нашёл штуки три похожих ошибки.
Раз такое дело — проверил соседние проекты, нашёлся аналогичный баг в BenchDotNet, в CodeJam тоже поправить надо будет, если попросят произвольные разделители.
·>И как этот код будет работать, если вдруг попадёт на какой-нибудь server side?
Никак. Поэтому все пишут свои ассерты
Здравствуйте, Sinix, Вы писали:
S>·>В нормальной ситуации — использовать коллекцию, которая не допускает null-элементы (кидает NRE например, при попытке добавить таковой) — чтобы обнаруживать косяк в момент его возникновения, а не потом когда-нибудь, если повезёт. S>Как по мне, подход в стиле "задача не решается легко == косяк в дизайне" годится только для сферического дизайна в вакууме.
S>В реальной жизни S>... S>Так вот, когда пишут код без оглядки на подобные ограничения, вечно получается абсолютно неадаптированное к реальной жизни решение. Не надо так делать
Конечно, в реальной жизни есть разные задачи. Ты, думаю, прекрасно понимаешь, что одни и те же задачи можно решать используя разные инструменты. Так вот, мой тезис в том, что инструмент "ассерты" — плохой инструмент, вместо него в подавляющем большинстве случаев желательно выбирать другие — исключения, явные проверки, логгеры, тесты, рефакторинг, редизайн и т.д.
Это как "сопли" плохой материал для скрепления предметов, лучше взять клей или гвозди.
S>·>Почему не вариант? Это говорит о том, что ассерт написать проще, чем ютест. А это говорит о том, что ютесты пишутся сложно. А это говорит о том, что код не самого лучшего качества. S>См выше про сферический дизайн. Довод "это у вас код неправильный" — первейший признак сабжа. S>Если коротко, то ассерты ортогональны тестам. Тесты документируют и проверяют контракт отдельных кусков кода. Ассерты обеспечивают соблюдение инвариантов для _всех_ реальных сценариев использования. Каждый инструмент решает свою задачу, смысл тут спорить-то?
Тесты должны проверять _все_ реальные сценарии использования. Как минимум те, до которых додумался программист (а раз он додумался до ассерта, то значит и до теста мог додуматься).
S>>>Или как в нашем случае — чтоб воспроизвести баг, нужно продублировать весь сценарий, т.к. итоговая ошибка вызвана мелкими логическими несостыковками в 3 независимых кусках кода. Ок, воспроизвели, починили, завели тест, всё зелёное. Какие гарантии, что эта же ошибка не воспроизведётся при другой комбинации условий и как этот момент вообще отловить? S>·>Я не понял из описания issue взаимосвязь с ассертами. Можно поподробнее? S>Очень просто — наш проект упал с ассертом "строка в .csv содержит неэкранированный разделитель", текущая культура Central Kurdish, разделитель списка — ؛. Заметь, что для этого нам не пришлось писать отдельный тест для csv, перебирать все культуры (эта, к примеру, появилась в восьмёрке, пока CI-сервер был на 2012-м — фиг бы поймали), просто прогнали интеграционный тест и он нашёл штуки три похожих ошибки.
А почему "строка в .csv содержит неэкранированный разделитель" нельзя проверить авто-тестом? Почему ассерт написали, а тест — нет?
Поменял культуру — програл test suite — посмотрел что упало.
S>Раз такое дело — проверил соседние проекты, нашёлся аналогичный баг в BenchDotNet, в CodeJam тоже поправить надо будет, если попросят произвольные разделители.
S>·>И как этот код будет работать, если вдруг попадёт на какой-нибудь server side? S>Никак. Поэтому все пишут свои ассерты
Да перестаньте их уже писать!
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ·, Вы писали:
·>Тесты должны проверять _все_ реальные сценарии использования. Как минимум те, до которых додумался программист (а раз он додумался до ассерта, то значит и до теста мог додуматься).
Я ещё раз повторюсь — все сценарии реально проверить на мелких проектах. На средних (один человек чисто физически не может охватить всю codebase) это нереально в принципе.
Поэтому остаётся или "что не проверили — ну и фиг с ним", или "пишем проверку, которая сработает при первой же попытке нагадить" и ждём теста / кода, который попытается.
Вся разница, что без ассертов попытка поломать код пройдёт незамеченной.
Кого-то это устраивает, кого-то нет.
·>А почему "строка в .csv содержит неэкранированный разделитель" нельзя проверить авто-тестом? Почему ассерт написали, а тест — нет? ·>Поменял культуру — програл test suite — посмотрел что упало.
Потому что нет никакого смысла воспроизводить все мыслимые и немыслимые ситуации для каждого _отдельного_ кусочка кода. Комбинаторный взрыв получается.
Как вы себе представляете тесты, которые должны проверяют корректность работы софта хотя бы в половине из случаев из вот этого списка?
Комбинацией из ассертов + интеграционными тестами такие вещи ловятся на раз-два. Кто ленится — получает вот
Здравствуйте, Sinix, Вы писали:
S>·>Тесты должны проверять _все_ реальные сценарии использования. Как минимум те, до которых додумался программист (а раз он додумался до ассерта, то значит и до теста мог додуматься). S>Я ещё раз повторюсь — все сценарии реально проверить на мелких проектах. На средних (один человек чисто физически не может охватить всю codebase) это нереально в принципе. S>Поэтому остаётся или "что не проверили — ну и фиг с ним", или "пишем проверку, которая сработает при первой же попытке нагадить" и ждём теста / кода, который попытается. S>Вся разница, что без ассертов попытка поломать код пройдёт незамеченной. S>Кого-то это устраивает, кого-то нет.
Да нет жеж. Пиши, проверяй, ломай, но без ассертов. Заменяй ассерты чем-то другим. Почему ты считаешь их незаменимыми? Используй другие инструменты.
S>·>А почему "строка в .csv содержит неэкранированный разделитель" нельзя проверить авто-тестом? Почему ассерт написали, а тест — нет? S>·>Поменял культуру — програл test suite — посмотрел что упало. S>Потому что нет никакого смысла воспроизводить все мыслимые и немыслимые ситуации для каждого _отдельного_ кусочка кода. Комбинаторный взрыв получается. S>Как вы себе представляете тесты, которые должны проверяют корректность работы софта хотя бы в половине из случаев из вот этого списка?
Если и не тесты, то логгер, например, может помочь решить эти проблемы.
S>Комбинацией из ассертов + интеграционными тестами такие вещи ловятся на раз-два. Кто ленится — получает вот
в подарок.
В проде он ничего не получит, получит unspecified behaviour, т.к. по определению в проде ассерты отключаются. А в дебаге он получит что-то лишь в том случае, если _догадается_ написать ассерт. А раз догадался написать ассерт — почему не написал тест?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ·, Вы писали:
·>Да нет жеж. Пиши, проверяй, ломай, но без ассертов. Заменяй ассерты чем-то другим. Почему ты считаешь их незаменимыми? Используй другие инструменты.
Дык какие же? Помимо юнит-тестов в смысле. Логгер — не вариант, очевидно. Кто за его выхлопом следить-то будет?
S>>Комбинацией из ассертов + интеграционными тестами такие вещи ловятся на раз-два. Кто ленится — получает вот
в подарок. ·>В проде он ничего не получит, получит unspecified behaviour, т.к. по определению в проде ассерты отключаются.
Ну, это догматизм ненужный. Если разработчик решает отключить ассерты в продакшне (не выборочно, только тяжёлые, а вообще все) — то обвинять надо точно не ассерты.
·>А в дебаге он получит что-то лишь в том случае, если _догадается_ написать ассерт. А раз догадался написать ассерт — почему не написал тест?
Потому что далеко не всегда получается придумать способ нарушить ассерт на текущем коде, иногда это в принципе невозможно. "Что-то может пойти не так" постфактум. Я давал уже кучу примеров — и с локалью, которая появилась после написания кода, и с багом с "потерянным" часом в новом году, и с "неожиданной" обработкой путей с точкой в конце.
Как-ещё по вашему можно защищаться от подобных ошибок?
Здравствуйте, Sinix, Вы писали:
S>·>Да нет жеж. Пиши, проверяй, ломай, но без ассертов. Заменяй ассерты чем-то другим. Почему ты считаешь их незаменимыми? Используй другие инструменты. S>Дык какие же? Помимо юнит-тестов в смысле.
Почему "помимо"?
S> Логгер — не вариант, очевидно. Кто за его выхлопом следить-то будет?
Кого настроишь — тот и будет следить. Лог-аппендеры при желании могут даже окошко могут нарисовать.
S>>>Комбинацией из ассертов + интеграционными тестами такие вещи ловятся на раз-два. Кто ленится — получает вот
в подарок. S>·>В проде он ничего не получит, получит unspecified behaviour, т.к. по определению в проде ассерты отключаются. S>Ну, это догматизм ненужный. Если разработчик решает отключить ассерты в продакшне (не выборочно, только тяжёлые, а вообще все) — то обвинять надо точно не ассерты.
Мда, похоже, тут спор частично терминологический. Под ассертами понимаются обычно такие проверки, которые отрубаются в проде. А если это такие проверки, которые работают всегда и везде, кидая исключение, то они ничем неотличимы от обычных проверок типа if(x) throw Exception, кроме, может быть, синтаксиса.
S>·>А в дебаге он получит что-то лишь в том случае, если _догадается_ написать ассерт. А раз догадался написать ассерт — почему не написал тест? S>Потому что далеко не всегда получается придумать способ нарушить ассерт на текущем коде, иногда это в принципе невозможно.
Если это и вправду "в принципе невозможно" — то накой тогда ассерт?
S>"Что-то может пойти не так" постфактум. Я давал уже кучу примеров — и с локалью, которая появилась после написания кода, и с багом с "потерянным" часом в новом году, и с "неожиданной" обработкой путей с точкой в конце. S>Как-ещё по вашему можно защищаться от подобных ошибок?
Чем не устраивает то что я перечислял уже — тестами, логгированием, корректной обработкой таких ошибок, етс?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ·, Вы писали:
·>Здравствуйте, Sinix, Вы писали:
S>>·>Да нет жеж. Пиши, проверяй, ломай, но без ассертов. Заменяй ассерты чем-то другим. Почему ты считаешь их незаменимыми? Используй другие инструменты. S>>Дык какие же? Помимо юнит-тестов в смысле. ·>Почему "помимо"?
Потому что есть класс ошибок, для которых писать нормальный тест невыгодно, а частичный тест ничем не поможет. (Например, код используется в десятке мест, надо гарантировать, что ни одно из них не передаёт, скажем, 42).
S>> Логгер — не вариант, очевидно. Кто за его выхлопом следить-то будет? ·>Кого настроишь — тот и будет следить. Лог-аппендеры при желании могут даже окошко могут нарисовать.
А зачем окошко-то? Ассерты здорового человека просто бросают исключение + дают возможность залогировать сработавшие ассерты.
Логгер плох тем, что теперь любой тест должен этот логгер настраивать и проверять, иначе ошибка останется незамеченной. Если настроить так логгер по умолчанию — получаем всё те же ассерты, только ненадёжные. Повезёт — ошибка словится, пользователь перенастроил? Ну, не судьба.
·>Мда, похоже, тут спор частично терминологический. Под ассертами понимаются обычно такие проверки, которые отрубаются в проде. А если это такие проверки, которые работают всегда и везде, кидая исключение, то они ничем неотличимы от обычных проверок типа if(x) throw Exception, кроме, может быть, синтаксиса.
А, это из плюсов, наверно, в наследство досталось. Глянь тут или тут. Везде основное свойство ассертов — "проверяет ожидания программиста", а не "не работает в релизе".
S>>·>А в дебаге он получит что-то лишь в том случае, если _догадается_ написать ассерт. А раз догадался написать ассерт — почему не написал тест? S>>Потому что далеко не всегда получается придумать способ нарушить ассерт на текущем коде, иногда это в принципе невозможно. ·>Если это и вправду "в принципе невозможно" — то накой тогда ассерт?
Ну вот смотри: пишешь ты код и видишь, что если вызвать метод DoWork() до метода Init() или после метода Clear(), то он натворит полную фигню. Смотришь код — везде всё норм, даже тест с моками есть. Оставляешь как есть — и в продакшне код творит полную фигню из-за бага сериализатора, который неправильно восстановил поле-структуру за десять вызовов по стеку от твоего кода. Не придумываю, реальный случай был лет так пять назад.
Здравствуйте, Sinix, Вы писали:
S>>>Дык какие же? Помимо юнит-тестов в смысле. S>·>Почему "помимо"? S>Потому что есть класс ошибок, для которых писать нормальный тест невыгодно, а частичный тест ничем не поможет. (Например, код используется в десятке мест, надо гарантировать, что ни одно из них не передаёт, скажем, 42).
Так ассерт не может в принципе "гарантировать, что ни одно из них не передаёт, скажем, 42". Он будет лишь что-то (не)делать, если 42 таки передалось.
А вот тесты — будут гарантировать: ведь ты явно выделил 42 как некий специальный случай, а значит для него будет специальный тест.
S>·>Кого настроишь — тот и будет следить. Лог-аппендеры при желании могут даже окошко могут нарисовать. S>А зачем окошко-то? Ассерты здорового человека просто бросают исключение + дают возможность залогировать сработавшие ассерты.
А теперь, дети, посмотрите на ассерты майкрософта.
S>Логгер плох тем, что теперь любой тест должен этот логгер настраивать и проверять, иначе ошибка останется незамеченной. Если настроить так логгер по умолчанию — получаем всё те же ассерты, только ненадёжные. Повезёт — ошибка словится, пользователь перенастроил? Ну, не судьба.
Ну обычно ERROR уровень таки не оставляют незамеченным.
S>·>Мда, похоже, тут спор частично терминологический. Под ассертами понимаются обычно такие проверки, которые отрубаются в проде. А если это такие проверки, которые работают всегда и везде, кидая исключение, то они ничем неотличимы от обычных проверок типа if(x) throw Exception, кроме, может быть, синтаксиса. S>А, это из плюсов, наверно, в наследство досталось. Глянь тут
Так там я это и прочёл:
When a program is deployed to production, assertions are typically turned off
Most languages allow assertions to be enabled or disabled globally, and sometimes independently. Assertions are often enabled during development and disabled during final testing and on release to the customer.
S>или тут. Везде основное свойство ассертов — "проверяет ожидания программиста", а не "не работает в релизе".
А ты сам-то читал?
Assertions should never stay in production code. If a particular assertion seems like it might be useful in production code, then it should not be an assertion; it should be a run time error check, i.e. something coded like this: if( condition != expected ) throw exception.
If you're even thinking of leaving assertions on in production, you're probably thinking about them wrong
Normally, you don't want users to see assertion messages in production code; assertions are primarily for use during development and maintenance. Assertions are normally compiled into the code at development time and compiled out of the code for production. For highly robust code, assert and then handle the error anyway.
S>>>·>А в дебаге он получит что-то лишь в том случае, если _догадается_ написать ассерт. А раз догадался написать ассерт — почему не написал тест? S>>>Потому что далеко не всегда получается придумать способ нарушить ассерт на текущем коде, иногда это в принципе невозможно. S>·>Если это и вправду "в принципе невозможно" — то накой тогда ассерт? S>Ну вот смотри: пишешь ты код и видишь, что если вызвать метод DoWork() до метода Init() или после метода Clear(), то он натворит полную фигню. Смотришь код — везде всё норм, даже тест с моками есть. Оставляешь как есть — и в продакшне код творит полную фигню из-за бага сериализатора, который неправильно восстановил поле-структуру за десять вызовов по стеку от твоего кода. Не придумываю, реальный случай был лет так пять назад. S>Не дешевле было просто ассерт поставить?
Лучше в DoWork вставить if(!inited) throw Exception.
У тебя публичный контракт с тремя методами Init/DoWork/Clear — а соответственно любой пользователь класса может не следовать этому контракту, значит его надо явно проверять. То что у тебя на данный момент в коде нет таких пользователей это тебе не даёт право надеяться на авось — такой плохой пользователь может получиться позже — из-за неудачного рефакторинга или добавления новой функиональности, а в твоём случае такой пользователь был, но был неявный, из-за рефлексии в этом сериализаторе, видимо.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
1. Таки спасибо, интересный спор получился У меня появляется настойчивое подозрение: главная причина спора в том, что ты понимаешь под ассертами что-то большее, чем просто "проверка в коде". Т.е. спор в итоге чисто про терминологию.
2. По пунктам.
·>Так ассерт не может в принципе "гарантировать, что ни одно из них не передаёт, скажем, 42". Он будет лишь что-то (не)делать, если 42 таки передалось.
Может-может. Достаточно покрыть тестами основные сценарии использования и можно с относительной уверенностью говорить, что критичной для нашего кода ошибки нет. Даже если ошибка и вылезет, скажем, у одного из клиентов, то оставленный в коде ассерт сразу её отловит и сообщит куданадо (с помошью клиента или автоматом — это уже детали реализации). В ближайшем патче ошибку исправят.
Сам понимаешь, при таком раскладе шанс поймать ошибку на порядки (серьёзно на порядки) выше, чем когда возможность ошибки проверяется в одном тесте из тысячи.
Как дополнительный бонус — у нас значительно вырастает полезность тестов, особенно интеграционных. Они проверяют не только входы-выходы, но и корректность кода в целом.
S>>А зачем окошко-то? Ассерты здорового человека просто бросают исключение + дают возможность залогировать сработавшие ассерты. ·>А теперь, дети, посмотрите на ассерты майкрософта.
Угу, это тяжёлые наркотики, не надо на них смотреть. Я ж говорю — проблема в том, что каждый норовит под ассертом понимать что-то сверх "опционально отключаемая проверка в коде".
·>Ну обычно ERROR уровень таки не оставляют незамеченным.
Юнит-тестами? Да легко. Кто в них лог проверяет?
S>>·>Мда, похоже, тут спор частично терминологический. Под ассертами понимаются обычно такие проверки, которые отрубаются в проде. А если это такие проверки, которые работают всегда и везде, кидая исключение, то они ничем неотличимы от обычных проверок типа if(x) throw Exception, кроме, может быть, синтаксиса. S>>А, это из плюсов, наверно, в наследство досталось. Глянь тут ·>Так там я это и прочёл:
А вот не надо выборочно цитировать Выделил:
When a program is deployed to production, assertions are typically turned off to avoid any overhead or side effects they may have…
Если выделенное не является проблемой для софта — смысл отключать ассерты?
S>>или тут. Везде основное свойство ассертов — "проверяет ожидания программиста", а не "не работает в релизе". ·>А ты сам-то читал?
О, снова неполная цитата. Там же дальше написано:
Normally, you don't want users to see assertion messages
...
For highly robust code, assert and then handle the error anyway.
Что считать robust code — это уже вопрос предпочтений, про сообщать пользователю — это чисто вопрос реализации, насильно спамить никто не заставляет.
Мы предпочитаем отслеживать ошибки даже в продакшне, а не пропускать их молча, но это от реальных требований зависит.
S>>Не дешевле было просто ассерт поставить? ·>Лучше в DoWork вставить if(!inited) throw Exception.
Блин, мы по кругу ходим. Я ж уже спрашивал: чем ассерты от исключений отличаются? Кроме того, что их можно переиспользовать без копипасты и выборочно исключать в критичных для производительности местах?
·>У тебя публичный контракт с тремя методами Init/DoWork/Clear — а соответственно любой пользователь класса может не следовать этому контракту, значит его надо явно проверять. То что у тебя на данный момент в коде нет таких пользователей это тебе не даёт право надеяться на авось — такой плохой пользователь может получиться позже — из-за неудачного рефакторинга или добавления новой функиональности, а в твоём случае такой пользователь был, но был неявный, из-за рефлексии в этом сериализаторе, видимо.
Ну да, о чём и речь. Тут никак без ассерта/проверки не обойтись, одни тесты никак не помогут.