Комментарии 102
Конечный код, который формирует http ответ, или рисует гуй или ещё что-то тестировать смысла нет. И вот теперь почему:
1. Тесты это действительно код, и код которые требует затрат на содержание.
2. Количество тестов растёт. И затраты на них тоже. Часто можно обнаружить что тесты растут быстрее самого приложения. Мы очень редко удаляем код, в тесты удаляются ещё реже. Когда есть тысяча работающих тестов, никто не пойдёт удалить 500 из них. Да, начинается деление на сьюты постоянных и страховочных, но затраты на содержание никуда не уходят.
Ну и самый главный вопрос, сколько тестов писать: ровно столько, сколько нужно для вашей персональной уверенности, с небольшой оглядкой на команду. В идеале тестов должно быть столько чтобы каждый член команды был уверен что всё ок. И это не 100% покрытие Это скорее что-то около 10%. Ядра системы как правила меньше чем куча нашлёпок по интеграции внешних апи, гуи и прочего одноразового кода. Чем чаще что-то меняется тем нужнее там тест(и помните что цена растёт с частотой изменений). А чем больше тестов, тем выше цена поддержки. Таким образом стоимость поддержки тестов на часто меняющуюся систему растёт, от времени, сверхлинейно, и никогда не имеет тенденций к снижению.
Поддерживаю по всем тезисам. И это всё — собственно, собирательная причина того, что TDD сложно назвать каким-то другим словом, кроме как "культ". Действительно, хорошие и полезные тесты — хороши и полезны, но на их создание уходят значительные усилия, и покрытие такими тестами будет скорее всего сильно меньше 100% (но покрытие — вообще не тот параметр, за которым следует гнаться). Все остальные тесты — как минимум немножечко вредят, а не помогают. Как максимум — вредят очень сильно. Например, толстая пачка посредственных тестов, порожденных адептом TDD при написании кода — вредит немножечко, но стоит только эту пачку поддерживать вдолгую — и она уже начинает вредить сильно, отжирая время на поддержку, обновление, перестановку всех граничных случаев, и т.д.
И очень редко когда люди применяют TDD, и при этом задают себе подобные вопросы, считают деньги и меряют усилия. А тесты — это инструмент, причем инструмент не единственный (ну, собственно частично об этом и данный пост). Его применение нужно оценивать.
а считали ли вы деньги на разработку и поддержку тестов, а спрашивали ли вы заказчика, который вам оные деньги дает, на что лучше их потратить, на тесты, или на развитие функциональности?
а если продолжить…
— а считали ли вы, сколько времени затрачивается на систему контроля версий, бранчи, слияния, пул-реквесты и т.д. и готов ли заказчик за это платить?
— а считали ли вы, сколько времени затрачивается на условный jenkins, его интеграцию с cvs, настройку и т.д. и готов ли заказчик за это платить?
— а считали ли вы, сколько времени затрачивается на условный elk, его настройку, сопровождение и т.д. и готов ли заказчик за это платить?
— …
И дальше продолжаем по аналогии:
И очень редко когда люди применяют <подставить по вкусу>, и при этом задают себе подобные вопросы, считают деньги и меряют усилия. А <подставить по вкусу> — это инструмент, причем инструмент не единственный (ну, собственно частично об этом и данный пост). Его применение нужно оценивать.
Минус не мой, если чо, хотя я с такой постановкой и не согласен. Смысла в таком обобщении не очень много, на мой взгляд.
имеет ли место культ вокруг TDDРаботал я как-то в одной крупной международной компании, разрабатывавшей 10+ лет систему управления резервным копированием, и там в то время заказали внешней консультационной фирме оптимизацию процесса SCRUM-разработки. Внешняя фирма тщательно разработала SCRUM-процедуры и структуру SCRUM-команд, только её разработки никак не учитывали специфику работы компании-заказчика в виде обработки реквестов «повышенной срочности» от заказчиков на исправление ошибок, оптимизацию, доработку и новую функциональность (это были в основном крупные корпоративные заказчики, если что) в части выделения на это человеко-часов. Но несколько миллионов долларов за работу были уплачены.
Осмыслив это, я понял, что культы модных методик и технологий — это, кроме прочего, прибыльная сфера по части преподавания и консультаций, так что такие культы всегда будут надуваться и поддерживаться.
Jenkins используется, а elk нет. Это в этом проекте. А кое где и дженкинса нет. Потому что не каждая возможность должна быть реализована. А вы устанавливаете следилку за монитором сотрудника или нет? А СБ проверяет до 10 колена всех? А ТРИЗ внедряете? А канбан одновременно со скрамом используете?
И очень редко когда люди применяют <подставить по вкусу>, и при этом задают себе подобные вопросы, считают деньги и меряют усилия.
Возможно что-то не так в вашем окружении, потому что в бизнесе, перед тем как что-то использовать считают деньги и меряют усилия постоянно.
а пул реквесты — нет.
А как тогда вы проводите рецензирование кода? Вообще ситуации бывают совершенно разные — в одной фин. конторе рецензирование проводят постфактум (после слияния). И ничего, живут так давно.
Я последние пару лет очень много занимаюсь этим и вижу, что да, раз в 5-10 PR'ов что-то нахожу, что следует исправить — народ соглашается. Если бы не находил, то перестал бы проверять тем или иным образом.
Я писал пару проектов один. И никто это не смотрел - некому было. Однако результат как у вас, сдано и работает. Мораль простая - главное без фанатизма.
Да, и тестов не было, кстати. Три года
Да, но сравнивать их надо не между собой а со стоимостью комплекса мероприятий по их предотвращению помноженной на реальную эфективность таких мероприятий.
Это разные люди, поэтому если они ведут себя как «экономически рациональные субъекты» (это хорошее приближение для описания людей), то рецензировать не выгодно => все пытаются оттянуть это как можно дальше => затягивают сроки вывода изменения в PROD.
Вообще теория игр как нельзя лучше подходит для описания работы больших контор. «Прикладная политика».
Учтите, что рецензирование в таких конторах «не оплачивается» — вы просто тратите своё время на помощь коллегам. Для руководства же вы просто продолбали время.
И конкретно в моём случае в текущий момент система контроля версий есть, бранчи и слияния есть
Ок, значит вы уже посчитали, сколько вам проносит пользы использование cvs.
Поделитесь, сколько денег сэкономили заказчику?
Также про jenkins. Заказчик знает сколько денег экономит, используя jenkins?
Заказчик знает сколько денег экономит, используя jenkins?<сарказм>
Заказчик знает сколько денег экономит конкурентам, используя jenkins?
</сарказм>
Система контроля версий и условный Jenkins — это фактически внешняя коллективная часть IDE, и они требуют времени только во время установки и настройки, а дальше его сильно экономят. Выбор «на что потратить деньги — на развитие функциональности или на систему контроля версий» вообще никогда не стоит: затраты времени несопоставимы, это как «купить автомобиль или пачку сигарет».
А почему ядро приложения более важно, чем не-ядро? Если повалится что-то в не-ядре, Вы пользователю тоже вот так объясните? :)
Потому что когда отвалилось ядро отвалилось множество функций (одноразовому коду в ядре делать нечего) и часто приложение неработоспасобно или существенно деградировало, а когда отпала одна функция приложения ничего критичного с бизнесовой точки зрения как правило, не происходит, как отпало так и починим. В полном соответствии с принципом graceful degradation. Мы всё равно не умеем делать приложения без ошибок, даже НАСА и банки не умеют. Но если посмотреть на стандарты написани кода на плюсах в НАСА, которые безусловно сильно снижают шанс на ошибку, то застрелиться так писать, это очень дорого. И проект где я работаю не смог бы окупить разработку по стандартам наса, а если бы это и сделал то не заработал бы больше. А всё потому что разработка это про деньги.
Если разработка это про деньги, то это как раз повод чтобы делать хорошо, нет? Я не знаю о стандартах NASA, я знаю только о процессах Amazon/Google. И Google предпочитает делать хорошо — и если на тесты нужно время, значит нужно его запланировать.
ИМХО ссылаться на graceful degradation, как на оправдание забиванию на тесты, несколько все же странно.
А почему Ваш проект не позволяет вложить чуть больше времени в достижение более-менее сносного покрытия юнит тестами (ну хотя бы >80%, хотя это очень мало)?
Зачем? Это не позволит ему больше зарабатывать. Есть очень много очень глючных продуктов которые зарабатывают. Мой не самый глючный. Но в вопросе тестов мир не бинарен, есть или нет. Вопрос сколько. Это тоже самое сколько безопасности должно быть в машине. И ответ "вся" не подходит. Потому что пятиточечные ремни точно безопаснее чем трёхточечные, но их никто не будет использовать. И вообще безопаснее не выезжать из гаража. И поэтому каждый ищет некое своё среднее. Общие стандарты растут, но низкобюджетные можели как были существенно менее безопасны чем лендровер, вольво и мерседес так и остаются. И всё это на рынке. И роллсройс например концы с концами не сводит, хотя очень безопасен. Возможно, трать они на это чуть меньше сил, как продукт они были бы незначительно хуже, а как бизнес значительно лучше.
Вопрос, почему продукт не сводит концы с концами, может быть вообще не связан с технической частью. Может быть фейл маркетинга и т.д.
И про "бинарность" я не говорил.
Про гараж вообще сравнение, простите, не в тему. Конечно, безопаснее вообще не писать код и не разрабатывать продукт, тогда и багов не будет, но я-то ратовал, наоборот, за обратное.
А что, если не секрет, у Вас за проект?:)
Онлайн и мобильные игры.
Кстати, а мутационное тестирование пробовали? Вот это, имхо, близко к абсолютному качеству тестов. Хотел бы я на такой проект посмотреть и может поработать, узнать как там со скоростью разработки и гибкостью.
О, вот мы и нащупали вашу точку баланса, сколько тестов надо :) у меня она просто в сторону меньшего количества тестов сдвинута.
Нет не приходилось. :) Как-то все в компаниях, где я работаю, принято unit & integration тесты и все (ну и load тесты, конечно). И плюс сложная система деплоя в связке с метриками и алармами — все вместе это дает довольно надежный Continuous Delivery с минимумом откатов.
И я утверждаю что покрыть тестами 80% кода займёт больше времени чем его написать. А это очень много.
Конечно больше, разве я спорю. Просто имхо это неплохо, но это, очевидно, вопрос вкуса, времени и традиций разработки в каждой компании.
А почему Ваш проект не позволяет вложить чуть больше времени в достижение более-менее сносного покрытия юнит тестами (ну хотя бы >80%, хотя это очень мало)?
Даже 100% покрытие не покрывает все пути выполнения. См. пример в статье. Полное покрытие путей может быть только если мы используем property testing, то есть, гоняем Монте-Карло, и количество задаваемых параметров обозримо. Тогда да,
мы получаем сообщение от G∀ST:
[["«propertyEQ» Proof: success for all arguments after 127 tests
Но обычно поле параметров слишком велико, поэтому доказательство невозможно даже за сутки прогона теста.
Это означает, что, как правило, мы должны принять волевое решение по прекращению работы над тестами. А для принятия этого решения необходимо взвешивать все плюсы тестирования и минусы, а потом прикидывать, где оптимум.
И оптимум сильно зависит от поля, в котором работает команда.
А нет цели покрыть все пути. Есть цель — снизить вероятность ошибки в продакшне. И еще, как я сказал выше, юнит тесты — отличный способ проверить, не совсем ли плохо написан тестируемый код.
Есть цель — снизить вероятность ошибки в продакшне.
Тогда возвращаясь к Вашему комментарию внизу:
1. Я сторонник того, что у нас есть много разных инструментов отфильтровывания ошибок. Они, в общем, образуют конвейер фильтрации.
2. Очень важно поставить эти инструменты в правильном порядке. Например дешёвые и грубые фильтры должны стоять вначале. А тонкие, но дорогие — в конце.
3. Для этого очень важно знать цену каждого инструмента. Вот на это и направлена статья.
Все верно, и я со всем согласен. Но юнит тесты обнаружат ошибку раньше чем все остальные инструменты. Я не понял аналогию с тонкими и грубыми фильтрами: разные инструменты отличаются вовсе не грубостью, а подходом. Интеграционные тесты не более и не менее грубые чем юнит — они другие. Но обнаружить ошибку юнит тестом выйдет дешевле чем интеграционным.
Лоад тесты тоже не более грубый и не более тонкий — они тестируют другое.
Но юнит тесты обнаружат ошибку раньше чем все остальные инструменты.
Компиляция раньше. Например, в коде
-- Наша мегафункция
eitherString :: Either String -> String
eitherString e = case e of
Left s -> s
-- Конец нашей мегафункции
компилятор скажет, что пропущен второй вариант.
Несомненно. Но компилятор ловит синтаксические ошибки, а тут-то разговор идет больше о логических.
Но и помимо компиляции в наших руках масса лёгких инструментов: простая пошаговая отладка, self-code-review, линтеры, анализаторы уровня Coverity, отладочная печать, блок-схемы на бумажке, обсуждение с товарищем. И наверняка я что-то забыл.
Но юнит тесты обнаружат ошибку раньше чем все остальные инструменты.
Это так
А нет цели покрыть все пути. Есть цель — снизить вероятность ошибки в продакшне.
это тоже так
Но обнаружить ошибку юнит тестом выйдет дешевле чем интеграционным.
если говорить про «обнаружить» то дешевле.
А потом мы можем попробовать сравнить две ситуации:
1. У нас есть только acceptance и integration тесты, но нет юнит тестов, и мы все баги будем ловить чуть позже.
2. У нас есть unit, acceptance, integration тесты и мы ловим ошибку раньше.
Так вот стоит сравнить стоимость содержания и написания unit тестов со стоимостью исправления бага после выявления его на acceptance или integration стадии. И внезапно может отказаться что более дорогое исправление багов в год в сумме может быть дешевле чем содержание юнит тестов. А может и не быть. И решать это команде и любой из этих подходов приемлем.
P.s. Почему-то среди программистов гораздо больше любят даже огромные предсказуемые затраты чем непредсказуемые. Это далеко не лучшая стратегия на рынке. Очень часто ограниченные и оценённые заранее непредсказуемые потери может быть дешевле просто оплачивать, чем содержать системы по их предотвращению. Это ровно то, почему супермаркеты в итоге выгоднее, хотя очевидно, сделай как в старых магазинах, где всё за прилавком и всё тебе подаёт продавец и краж от посетителей будет меньше. Но это не выгодно. Ещё очень много про принятие потерь и работу с ними можно почерпнуть из военной истории.
Это так
Это не так — вы сперва компилируете, а потом пишете тесты, или нет?
Когда как. Red green refactor иногда бывает но не постоянно. И да компилятор ккк я понимаю был все этих инструментов. Если сюда упоминать копилятор то я всенепременно за достаточно жеский компилятор, но не абсолютно. Я не фанат раста или других жестких языков.
Какие-то фильтры ошибок, конечно, можно переставлять в конвейере цикла разработки — если тесты уже написаны, разумно их запустить перед self-review или прогоном линтера.
Поэтому ультимативное утверждение, что юнит-тесты — это первичный фильтр ошибок, в корне неверно. В некоторых ситуациях может быть, но как правило, они где-то посередине цепочки.
Почему-то среди программистов гораздо больше любят даже огромные предсказуемые затраты чем непредсказуемые.Это типично менеджерский подход.
А ведь время итерации write-check-correct loop — это важнейшая характеристика, напрямую влияющая на производительность программиста.
Так а что вы предлагаете использовать вместо тестов если я хочу получить этот check?
Соответственно, после компиляции программы на том же Haskell/Ocaml уже очень мало чего надо проверять тестами => их становится мало => их неприятные побочные эффекты уже не страшны. К сожалению, это не очень хорошо работает с вычислительными задачами, где почти всё типа Double. :-(
То есть, они должны находиться в конвейере после системы типов, а не вместо неё
При написании кода возможны, условно говоря, 100 единиц проблем.
Система типов выделяет из них, условно говоря, 5 единиц.
При этом стоит, условно говоря 80 руб за единицу.
Система тестов выделяет, условно говоря, 80 единиц проблем (и выделяемые типами входят в эти 80)
При этом стоит 20 руб за единицу
Это мои эмпирические наблюдения
sqrt :: Double Double Double -> Double
sqrt eps x r
| abs (r * r - x) < eps = r
| otherwise = sqrt eps x ((r + x/r)/2)
Там же всё имеет один тип.
Вообще вещественная арифметика — одна из самых неприятных для работы вещей что в пруфассистантах, что во всяких SMT.
Да, насколько я знаю. Поэтому пока мне кажется разумным именно гонять разные Монте-Карло — в моём случае G∀ST. Конечно, очень неудобны Double — все эти граничные случаи и т.д. Я в какой-то момент для полиномов стал использовать поля Галуа — они хороши тем, что это совершенно нормальные числа, без каких-либо потерь точности (надо написать по этому поводу маленькую заметку). Но зато у них нет упорядочения. :-( То есть, < не работает.
Вообще идея полиморфных вычислительных алгоритмов мне кажется богатой. В принципе, понятно, как использовать в разных алгоритмах рациональные/алгебраические числа для их тестирования. А ещё полиморфизм открывает дорогу автоматическому дифференцированию, конструктивным числам и всяким UNUM (как хорошие интервальные).
Какая альтернатива интеграционным тестам на агде?
Греческие и спецсимволы лигатурами созданы или через буфер копируете в utf8?
и наверняка часть из претензий надуманаЧуть более чем все претензии надуманы. При всех недостатках альтернативы ещё хуже. Лучше 10 минут потерять пока тест гоняется, чем потом от пользователя прилетит. А пользователи они ведь разные бывают.
Не выявят всех ошибок, да, но выявят множество ошибок, которые были бы пропущены без тестов.
Трудоемкость, время, да, но искать баги вручную и отдавать кота в мешке в прод, где после очередного изменения гарантированно что-нибудь в неожиданном месте отвалилось и без тестов мы это не заметили, ещё хуже.
Завязано на программиста, который их пишет, да, если программист ответственный, то это как часть его хард-скиллз: использовать какие-то надежные приемы написания кода, использовать тесты,
Совершенно согласен. Увы, не могу плюсануть...
Первому тесты никакие не нужны, это потеря времени, второй без тестов закопается в самоподдерживающейся как лесной пожар системе из возникающих по мере изменения кода ошибок, провоцирующих другие ошибки.
В какой области считали на нем?
ЗЫ. Тесты не пишу, но у нас хранимки в БД — как для них вообще тесты писать?
ЗЫ. Тесты не пишу, но у нас хранимки в БД — как для них вообще тесты писать?
Может быть какие-нибудь стенды есть? Если честно, вот для такого бы я точно писал тесты. Чисто для душевного спокойствия.
Я так и не понял в чем недостатки. Ну да, тесты нужно писать — так ведь и руки чтобы помыть, нужно потратить немного энергии.
Для меня самый большой плюс юнит тестов даже не собственно в тестировании а в "валидации качества" моего кода. Обычно, если у меня не получается написать маленькие компактные юнит тесты, это является мощным сигналом подумать, а не плох ли мой код. Поэтому для меня, юнит тесты — это хорошо.
Во многих подразделениях компании Amazon у вас нет шанса пройти код ревью, если вы не пишете тесты на ваши изменения.
В гугле (в некоторых отделах) внутренняя код ревью тулза просто не позволит вам выложить код ревью, если нет юнит тестов.
Конечно, написание юнит тестов иногда напрягает, т.к. времени уходит больше чем на "полезный" код. Но, если отнестись к этому серьезно, то это помогает в будущем ловить ошибки, когда кто-то другой (вы ведь, как правило, не одни разрабатываете; с чего вы взяли что ваши коллеги так же аккуратны или. так же разбираются в этом коде как вы?) впопыхах делает изменение и не думает о каких-то специфических сценариях.
Как и везде — чтобы получить профит завтра нужно немного вложиться сегодня.
Я так и не понял в чем недостатки.
Очень жаль. В следующий раз буду писать лучше.
Вы знаете, я в статьях про тестирование наверное уже раз пять писал, что поступаю примерно так же. Примерно — потому что считаю, что лучший способ повышения качества кода — это как следует над ним подумать. В том числе — и тем способом, о котором вы говорите, т.е. написания тестов (если вам так удобно думать надо кодом, в терминах теста). А еще — более точное описание структур данных, которые применяются, если не для компилятора, то хотя бы для себя (ну типа, если у языка система типов недостаточно хороша). И я много раз замечал, что в процессе вот такого обдумывания качество кода повышается уже до написания какого-либо теста.
Конечно, думать над структурами данных и интерфейсами, как минимум, не менее важно, абсолютно согласен.
Но это не исключает того, что тесты тоже могут тут помочь. :)
И в процессе такого преобразования (чтобы написать тест, но еще до его фактического написания) я зачастую выясняю что-то о своем коде, что я не учел, забыл и т.п, и что позволяет мне проблему найти. И если я уже тест пишу — то лишь для того, чтобы тут же и убедиться, что проблема решена. Потому что код иногда в итоге становится настолько прозрачен, что смысла в тестах на него уже мало.
Из сферических наблюдений за кровавым энтерпрайзом, откуда все эти «бэст практисес» обычно растут (десятки независимых команд в различных таймзонах, разные конфигурации, мудреные интеграции с такими же монстрами и т.п.). Отловить дефект локально и исправить обычно занимает минуты или часы одного разработчика. Исправление дефекта, отловленного в pull request средствами CI может занимать часы или дни, в добавок отжирая время ревьюверов (которые часто находятся в других таймзонах). Регрессии на нижних энвайронментах могут отрабатываться днями и неделями, стоить времени уже десятка человек (здесь подключается low env саппорт, devops, разношерстный QA, дев команды и всякие неравнодушные менеджеры), приводить к простоям других недешевых ребят. Баги на продакшене отрабатывают толпы народа (к предыдущим добавляются три линии саппорта, релиз менеджмент, аккаунты и т.д. и т.п.), чреваты бритчами SLA, что может выливаться в еще большие потери в деньгах и репутации. Поэтому рациональное предложение молодого девелопера — а давайте тут закоммитим кусок кода без теста, я сто раз проверил этот флоу локально, сэкономим полчаса моего времени — это чисто напоржать. В других условиях все может быть по другому.
Я ещё не встречался ни с одной задачей, где у меня не получилось бы убедить тайпчекер, что всё корректно.
Ну для вычмата, всё-таки, юнит-тесты будут дешевле. По крайней мере, для меня. Бизнес-логика — это да, тут typechecker.
Интересно какие есть возможности, чтобы заменить тесты на верификацию, скажем клиента и сервера, общающихся между собой через REST API?
0xd34df00d Формальная спецификация (в виде системы типов или что мы там используем для верификации) это тоже формальная система, которая или неполна, или противоречива. Следовательно какие-то элементы мы вынуждены вводить как аксиомы или игнорировать на этом уровне (нюансы окружения в котором будет собираться или выполняться программа, различные нефункциональные требования и т.п.). Но даже со всеми этими допущениями мы упираемся в проблему останова — как формально доказать для произвольной программы, что она когда-нибудь завершится? Поэтому сколько не верифицируй, а тестировать все равно придется. Разве не так?
А теперь нужно вспомнить, сколько раз вы жалели о ненаписанных тестах, а сколько — о написанных зря.
Для определения "правильного" покрытия юнит-тестами у меня есть универсально-бесполезное правило: тестировать нужно код, который сломается. Если вы написали тест, который всегда будет "зеленым" — время на его написание и запуски было потрачено зря.
Другая сторона медали или про недостатки юнит-тестирования