Сколько стоят юнит тесты?

    image

    Сейчас, на пике экономического цикла в такой горячей отрасли как разработка ПО не принято считать деньги. Зачастую этот процесс в принципе позиционируется как творческая деятельность, где не надо ничего обосновывать, а художнику виднее, что и как писать. В частности, на тему юнит-тестов и TDD ведется много споров, но, к сожалению, все они скатываются к бездоказательным заявлениям и эмоциональным выпадам, подтверждаемым пруфами на удачно подобранные статьи и книги методологистов, зарабатывающих на консультациях и продажах тренингов, которые, в свою очередь, не содержат абсолютно никакой статистики или расчетов, либо, напротив, к огульным обвинениям в смузихлебстве и прочих грехах юности.

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

    Что такое стоимость


    Прежде всего, если мы собираемся рассчитывать стоимость чего бы то ни было, нужно сначала хотя бы в общих чертах понимать, что такое стоимость. Когда мы покупаем батон колбасы, такого вопроса не возникает, так как под ней висит ценник. Но в случае проекта мы имеем дело не с одноразовой оплатой, а с денежным потоком, продолжающимся длительное время, причем сначала вкладываем мы, а потом платят нам. Для того, чтобы корректно оценить его, нужно учесть, что деньги, полученные завтра будут стоить меньше, чем сегодня. Если бы колбаса продавалась по подписке, сделать это было бы не сложно, стоимость подписки можно было бы рассчитать, дисконтировав будущие выплаты за нее на величину инфляции. Однако в случае проекта нам нужно учесть, что компания, вкладывая в него деньги, ожидает не только компенсировать свои затраты, но и заработать, да так, чтобы покрыть свои риски. Есть огромное количество рисков, но в конечном счете все сводятся к тому, что возврат денег, вложенных в реализацию проекта не гарантирован и плохо предсказуем. Деньги можно отнести в банк или одолжить какому-нибудь крепкому заемщику и получить гарантии регулярной выплаты процентов, но в проект нельзя инвестировать так, чтобы в итоге получить предсказуемый поток выплат строго по графику. Поэтому с точки зрения инвестора, в данном случае компании, дисконтированный на требуемую ставку доходности денежный поток, генерируемый проектом, так же называемый чистой приведенной стоимостью, должен быть положительным:

    $0 < NPV = FCFF / WACC + FCFF / WACC ^ 2 + FCFF / WACC ^ 3 …$


    Для того, чтобы рассчитать его, нам понадобится знать, сколько денег принесет или съест проект в первый, второй, третий год и т.д., а также ставку дисконтирования, которая должна быть не только выгоднее, чем в банке, но и покрывать риски.

    На самом деле, это несколько упрощенная формула. Строго говоря, ставка будет меняться год от года, поэтому в знаменателе стоило бы использовать WACC1 * WACC2 * WACC3 и т.д., но на практике этим пренебрегают даже профессиональные оценщики, т.к. в силу методики расчета WACC, в сегодняшнюю ставку уже заложены ожидания рынка относительно будущих ставок и строить свои предположения на этот счет непродуктивно.

    Существуют разные типы денежных потоков, но я взял наиболее удобный для наших целей денежный поток на фирму, который учитывает не только деньги, причитающиеся собственникам, но и кредиторам. Конечно, большинство IT компаний не имеют заметных долгов просто потому, что без залога им никто не одалживает, а заложить им нечего, но все же есть и исключения, например, такой подход может быть удобен при оценке проекта в in-house разработке закредитованной производственной компании. Вторая причина, почему нам интересен именно FCFF заключается в простоте его расчета, FCFF это всего лишь операционная прибыль за вычетом налогов, чистых капитальных затрат и изменений в оборотном капитале.

    Поскольку FCFF это денежный поток одновременно и на собственников, и на кредиторов, дисконтируется он по взвешенной ставке стоимости капитала, как собственного, так и заемного.

    В крупных компаниях стоимость капитала отслеживает финансовый департамент, поэтому ее можно просто спросить, но для общего случая нам все же понадобится формула для расчета WACC:

    $WACC = Re * P / EV + Rd * (1 – P / EV)$


    Здесь Re – стоимость собственного капитала, Rd – стоимость заемного капитала (то есть эффективная ставка по долгам компании), P – рыночная стоимость собственного капитала, EV – общая стоимость предприятия (EV = P + D, где D – это долг).

    Дальше нам нужно определить Re, для этого есть разные модели, но проще всего взять модель CAPM, где Re = Rb + β * Premium, где Rb – это безрисковая ставка, Premium – это премия к доходности за инвестирование в собственный капитал, а не в заемный, а β – это коэффициент риска, который показывает, насколько более рискованным является наш проект относительно бизнеса некой усредненной компании.

    Как обеспечивается качество и что такое юнит-тесты


    Теперь нам нужно определиться с тем, что такое юнит-тесты. Как ни странно, многие люди, даже близкие к разработке, зачастую называют юнит-тестами любые автоматические тесты, но это, конечно же, не так.

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

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

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

    Эти меры способны обеспечить достойный уровень качества, но нет предела совершенству. То, чем занимаются тестировщики, называется тестированием черного ящика, в их обязанности не входит знать все особенности реализации, поэтому тестирование обычно сфокусировано на типичных сценариях и не ставит перед собой цель сломать систему или проверить ее поведение в каких-то нетипичных условиях. Кроме того, некоторые вещи нелегко проверить просто из-за отсутствия у них интерфейса, например, если целью итерации является разработка библиотеки для доступа к данным или какого-нибудь специфического API, для его тестирования нужно будет написать некое приложение или хоть что-то, что использовало бы этот компонент. В таких случаях приходится частично вернуть разработчикам функцию контроля качества и попросить их написать интеграционные тесты. Это уже второй тип автоматизированных тестов, которые применяются на проекте. Их целью является тестирование корректности взаимодействия компонентов системы, написанных разными людьми, тестирование поведения этих компонентов в пограничных условиях, а также корректности реакции на сбои в окружении.

    Что ж, у нас есть тестировщики, которые тестируют весь проект на соответствие требованиям, есть тесты для того, чтобы автоматизировать их работу и есть тесты, которые тестируют части проекта, написанные разными разработчиками, что можно сделать еще? Юнит-тесты как раз и претендуют на то, чтобы быть четвертым уровнем контроля качества. Они проверяют код, написанный одним программистом, причем, как правило, тестируется минимальная часть кода, в принципе пригодная для тестирования, например, отдельный класс. На практике чаще всего написанием юнит-тестов для собственного кода занимается сам разработчик, а их количество и необходимость контролируется слабо. По моим наблюдениям типичным количеством затрат времени разработчика на юнит-тесты можно назвать около 40% времени на разработку самой фичи, хотя это соотношение может сильно варьироваться. Широко известен кейс open source проекта SQLite, где из-за избытка низкоквалифицированной бесплатной рабочей силы, обеспечиваемой большим количеством желающих поработать над известным проектом, эта рабочая сила утилизируется армейским способом, то есть написанием бесполезных юнит-тестов, чей объем в какой-то момент в 100 раз превысил объем кода самой СУБД. Не являются чем-то удивительным и обратные случаи, когда юнит-тесты не пишутся или пишутся в минимальном объеме. В конце-концов, практически все ПО, разработанное до конца нулевых, то есть до эпохи аутсорсинга и Agile, было создано без юнит-тестов.

    Затраты, поправка на сложность и мифический человеко-месяц


    Разумеется, если нужно написать юнит-тесты или что-либо еще, придется либо выделить больше времени на проект, либо нанять дополнительных разработчиков. Главный вопрос, который при этом встает, является ли зависимость времени и стоимости разработки от количества кода линейной, или же она подчиняется другому закону.

    Когда-то давно у меня был бесплатный SVN репозиторий на небезызвестном сервисе Assembla, предоставлявшем услуги хостинга исходников и инструменты совместной работы, то есть трекер, статистику и прочую ерунду. Позже халява закончилась, но присылать новостные рассылки и оповещения они не перестали. Так вот, в 2015 году их сотрудница опубликовала короткий пост, который назывался “How many people should discuss a task?” Сейчас он сохранился только в Web Archive. Суть поста заключалась в следующем: сотрудница собрала статистику по клиентам, построив график зависимости продолжительности задачи от количества человек, которые ее обсуждали, результат оказался следующим:

    image

    Видно, что зависимость нелинейна. Два человека обычно вовлечены в решение задачи продолжительностью в два дня, три человека – четыре дня, а четыре человека – уже шесть дней. Что они там делают? Можно предположить, что задача требует нескольких этапов работ, например, в случае с двумя людьми, Вася делает свою часть задачи, а потом передает ее Пете, поэтому она и длится два дня. Трое человек уже могут поссориться и лишний день делить обязанности, выяснять, кто виноват и что делать, а группа из семи человек потратит на обсуждения, согласования и отфутболивание друг друга уже шесть дополнительных дней.

    Конечно, можно также предположить, что дружной команде из семи человек сложные задачи даются намного проще и чем больше человек заняты задачей, тем она может быть грандиознее, ведь дружба – это магия! Поэтому такие рассуждения могут выглядеть притянутыми за уши, и я не буду включать их в последующие расчеты, однако если вы хотите получить более консервативную оценку, не лишним было бы внести некую поправку на нелинейность роста затрат при росте кодовой базы проекта, в которую, безусловно, входят и юнит тесты, либо заложить некий запас прочности в требования к уровню NPV.

    Если объяснять нелинейность данного графика исключительно ростом размера команды, то связанные с ним издержки можно оценить по следующей таблице зависимости доли времени, потерянного на коммуникацию, от размера рабочей группы:

    image

    Например, если в команде пять разработчиков, и вы полагаете, что вам понадобится нанять двоих, чтобы каждый мог тратить дополнительно 40% своего времени на юнит тесты, будьте готовы к тому, что затраты на разработку могут вырасти больше, чем на 40%. Команда вырастет и станет менее эффективной, вместо 5 * 0,625 = 3,125 условных единиц производительности, она будет обладать 7 * 0,539 = 3,77 единиц, а объем работы увеличится с 1 до 1,4 условной единицы работы, соответственно, время, необходимое на разработку увеличится на 16%.

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

    Конечно, трудно утверждать, что нелинейность графика от Assembla связана только лишь с падением эффективности в результате роста команды, но она хорошо согласуется с интуитивным пониманием сложности и законом Брукса, поэтому, если вы не хотите рисковать и вам нужна именно консервативная оценка, эти данные могут стать хорошим подспорьем.

    Польза юнит тестов


    Кроме затрат, юнит тесты приносят и пользу. Конечно, в подавляющем большинстве случаев баг, который мог бы быть пойман юнит тестами, будет пойман на других уровнях контроля качества, но всегда есть вероятность технического сбоя и теоретически юнит тесты могут ее снизить. Лично мне такие случаи неизвестны, к счастью, все тестировщики, с которыми мне доводилось работать, были исключительно ответственными людьми, но, когда речь идет о настолько низких вероятностях, личный опыт может быть нерепрезентативен. Сбои могут иметь разные последствия, например, у компании может быть SLA, нарушение которого повлечет вполне определенные финансовые потери, скажем, компания будет вынуждена подарить клиентам в качестве компенсации один месяц бесплатного пользования ее сервисами, лишившись 1 / 12 части выручки. В этом случае ужесточение контроля качества, которое снижает вероятность нарушения SLA в течение года с 10% до 8%, снизит среднегодовые убытки примерно на 0,17% выручки. Эти деньги и будут тем положительным компонентом денежного потока, который необходимо добавить в модель.

    Обратите внимание, что такой простой подсчет применим только когда вероятность потерь невелика, если же вероятность выше 15-20% и может привести к банкротству или ликвидации компании, желательно использовать опционные модели оценки, например, такие как дерево решений. К счастью, в большинстве случаев какой-нибудь глупый баг — это не то, что может обанкротить компанию и нам не понадобится погружаться в ужас расчетов стоимости опционов.

    Пример первый: компания Бизон


    Бизон – крупный интернет-магазин, сами они называют себя онлайн ритейлером №1 в России. Компания не публична, однако в рамках недавней сделки по докапитализации ее общая капитализация была оценена в 50 миллиардов рублей, что вдвое больше годовой выручки. Докапитализация понадобилась в связи с операционными убытками, однако акционеры надеются выйти на рентабельность по операционной прибыли в 10% после того, как компании удастся завоевать более высокую долю рынка и удвоить выручку в течение года, после чего она должна будет начать зарабатывать, а рост выручки замедлится до 30% во второй год, 20% в третий год и, наконец, установится на уровне в 10% в четвертый и последующие годы. Впрочем, банки в этом не очень уверены и дают Бизону вдолг с осторожностью, общий долг компании составляет всего 10 миллиардов рублей по ставке 11%. Бизон достаточно неуклюжая и плохо управляемая на операционном уровне компания, бесконтрольный найм сотрудников уже привел к тому, что в ней работает 600 программистов, чей общий ФОТ составляет 1,5 миллиарда рублей в год и которые тратят на юнит тесты около 30% рабочего времени. У компании нет обязательств перед клиентами и технический сбой может привести только к временной остановке продаж, при этом в случае сбоя откат на старую версию сайта занимает около часа.

    Каков NPV от использования юнит тестов в Бизоне?

    Выручка Бизона должна составить 50, 65, 78 и 86 миллиардов в первый, второй, третий и четвертый год соответственно. Вероятность сбоя возьмем равной 33%, то есть инцидент, способный завалить их сайт надолго может произойти примерно раз в три года, что не так уж плохо. Допустим, использование юнит тестов может снизить ее до 25% просто потому что помимо ошибок разработчиков есть еще вероятность различных аппаратных сбоев, DDOS атак и прочих неприятностей. Если сайт интернет магазина недоступен в течение часа, ритейлер теряет не больше 0,023% выручки даже с учетом того, что покупатели активны в среднем только 12 часов в сутки. Иными словами, юнит тесты сокращают потери компании на 11,5 миллиона рублей в первый год, 14,8 во второй, 17,8 в третий и 19,6 миллиона в четвертый год.

    Даже без учета роста штата и зарплат разработчиков, затраты на юнит тесты составят 450 миллионов рублей в год.

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

    Вернемся к разработчикам, допустим, что ФОТ растет на 10% в год, тогда, суммарный эффект от использования юнит тестов составляет -438, -480, -527 и -579 миллионов рублей операционного убытка в первый, второй, третий и четвертый год соответствено, после чего убыток растет на 10% ежегодно. Юнит тесты в данном случае не влияют на чистые капитальные затраты и оборотный капитал, но убыток приводит к экономии на налогах, равной 20% от объема убытка, соответственно, его нужно умножить на 0.8: -351, -384, -421 и -463 миллиона рублей.

    EV компании составляет 50 + 10 = 60 миллиардов рублей, на P приходится 83% капитала, на D 17%, нам известно, что стоимость долга составляет 11% годовых, тогда для расчета WACC остается только лишь найти стоимость собственного капитала. Бизон работает в России, поэтому в качестве безрисковой ставки нужно взять эффективную доходность государственных облигаций с наибольшей дюрацией, сейчас это 7,6%. Премия за инвестирование в собственный капитал варьируется год от года, но обычно она находится в районе 4-6% годовых, мы возьмем 5%, а для определения коэффициента β обратимся к справочнику и найдем там безрычаговый коэффициент риска для компаний из отрасли онлайн ритейла (unlevered beta) равным 1,3. Но у Бизона есть хоть и небольшие, но долги, поэтому нужно сделать поправку и подсчитать рычаговую бету (levered beta):

    $βl = βu * (1 + (1 - T) * D / P) = 1,3 * (1 + (1 – 0.2) * 10 / 50) = 1,51$


    Таким образом ставка дисконтирования WACC составит

    $(7,6 + 1,51 * 5) * 0,87 + 11 * 0,17 = 15 процентов$


    Наконец, подсчитаем, сколько Бизону стоят юнит тесты, для этого дисконтируем первые годы неравномерного роста отдельно, а для последующих лет с ростом по 10% в год воспользуемся моделью Гордона.

    Приведенная стомость первого года составит $-351 / 1,15 = -305$ миллионов рублей, второго $-384 / 1,32 = -290$ миллионов, третьего $-421 / 1,52 = -277$ миллионов.

    Начиная с четвертого года убытки равномерно растут на 10% в год, соответственно после третьего года номинальный убыток от юнит тестов можно подсчитать по формуле $-463 / (1.15 – 1.1) = -9260$ миллионов, которые необходимо привести к первому году: $-9260 / 1,75 = -5291$ миллион рублей.
    В целом ущерб от использования юнит тестов составляет $305 + 384 + 421 + 5291 = 6,4$ миллиарда рублей.

    Пример второй: компания Гиперсталь


    Василий – подающий надежды выпускник Челябинского Колледжа Инновационных Технологий. Амазонов и Гуглов в Челябинске нет, зато есть множество сталелитейных компаний, в одну из которых ему и посчастливилось устроиться. Как выяснилось позже, бюджеты здесь скромные, денег хронически не хватает, поэтому она могла позволить себе нанять программиста только с зарплатой менее 50 тысяч рублей включая все налоги и обязательные выплаты. Первым заданием Василия стал софт для управления работой доменной печи. Этот проект должен занять не больше двух месяцев и вряд ли будет в дальнейшем поддерживаться и развиваться.
    В ходе визита Василия в цех ответственный за производство специалист сказал ему в общих чертах следующее: «Уважаемый коллега! Пожалуйста, обратите внимание на этот гигантский ковш с расплавленным металлом. Если что-то пойдет не так, мы будем не только крайне обескуражены, но и столкнемся с технологическими трудностями. Дело в том, что если домна встанет, металл внутри нее застынет и на ликвидацию последствий аварии придется потратить три месяца. Будет нелегко разобраться с гигантским куском металла в цеху и заменить его новой домной». Позже Василий выяснил, что аварийная остановка домны может обойтись компании в 8 миллиардов рублей.
    Вопрос: стоит ли Василию обеспокоиться написанием юнит-тестов?
    Поскольку у меня уже нет сил и терпения подсчитывать очевидное, сразу скажу ответ: конечно же да. У Василия нет опыта, вероятность допустить ошибку у него высока (даю процентов 50, что в одиночку без помощи коллег и без адекватного контроля качества его программа где-нибудь заглючит и процентов 10, что это приведет к аварии), его время ничего не стоит, а цена ошибки крайне велика. Поскольку в данном примере речь идет о коротком проекте, который будет написан и забыт, нет необходимости что-либо дисконтировать, достаточно сопоставить зарплату Василия за два месяца, равную 100 тысячам рублей и матожидание убытков порядка 10% * 8 миллиардов = 800 миллионов рублей.

    Пример третий: XSoft


    XSoft – успешная аутсорсинговая компания, которая только что заключила контракт с очередным западным заказчиком. Заказчик планирует нанять 7 программистов, его бюджет в этой части составляет 15 миллионов рублей в год, из которых XSoft заберет себе 3 миллиона. Заказчик – лопух и ничего не смыслит в разработке. С точки зрения XSoft, должны ли разработчики писать юнит тесты?

    Конечно же да! В данном случае затраты на написание и поддержку юнит тестов несет заказчик, а для подрядчика дополнительный объем работ означает лишь увеличение продолжительности проекта и дополнительную прибыль, которая как минимум пропорциональна количеству человеко-часов, затраченных на юнит тесты, а в лучшем случае растет опережающими темпами из-за увеличения кодовой базы и сложности проекта. С вашего позволения, я не буду развивать эту мысль дальше, копаться в интимных подробностях взаимоотношений аутсорсера с заказчиком и дисконтировать его денежные потоки, поскольку вывод и так очевиден.

    Послесловие


    Статья получилась большой, и я надеюсь, что старался не зря. Как и любой проект в любом бизнесе, решение о применении юнит тестов на вашем проекте должно быть экономически обоснованным. Когда компании в других отраслях планируют купить станок, открыть завод или магазин, они обязательно рассчитывают NPV и / или IRR. Печально видеть, насколько беспечной отраслью в этом плане остается IT. Зато знание основ финансов и привычка вовремя открывать Excel может дать вам заметное преимущество перед конкурентами.
    Поделиться публикацией

    Похожие публикации

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

      +5
      >Статья получилась большой, и я надеюсь, что старался не зря.
      Тема на самом деле очень интересная. И хотя выводы не новые, и достаточно очевидно, что платите вы за разработку тестов сегодня, а профит возможно получите потом, но я видел с десяток наверное статей прямо тут, где ультимативно утверждается: «Вы обязаны писать тесты». Ну то есть, очевидно это далеко не всем.

      Есть еще несколько аспектов, на которых может быть стоило бы акцентировать внимание:

      — вопрос не стоит упрощать до «писать или не писать юнит тесты». Правильная постановка ближе к чему-то такому: «Какой уровень надежности нам нужен, и как лучше его добиться?». То есть, следует рассмотреть разные способы повышения надежности. Тесты — не единственный.
      — кстати, а измеряли ли ли мы надежность на сегодня? Мы уверены, что она недостаточная? Умеем ли мы вообще ее мерять?
      — тесты не гарантируют отсутствие багов. Поэтому ответ на вопрос: «Стоит ли Василию обеспокоиться написанием юнит-тестов?» вполне может быть иным: нет, не стоит, все равно надежность от этого не вырастет, потому что Василий и тесты писать не умеет тоже. А нужно лучше поискать опытного консультанта, и провести допустим code review.

      Ну или даже еще проще и короче: у нас есть команда, и ее возможности ограничены (финансовый аспект, что вы рассмотрели, вообще говоря не единственный, время, как правило, вообще не купишь). В зависимости от вида проекта, квалификации людей в команде и пр. факторов, мы можем потратить деньги и время команды на разные вещи. И не факт, что именно на тесты.
        +1
        Всё это так, но если вы хотите получить какие-то осмысленные результаты, то нужны какие-то вменяемые исходные данные. Если вы рассматриваете вариант, когда у вас надёжность в компании с «бесконтрольным наймом сотрудников» и без написания юниттестов получается на уровне самых-самых лучших компаний, которые вкладывают в тестирование миллиарды… то тут вряд ли можно говорить о чём-то ином, чем подгонка исходных данных под ответ.
          +1
          Конечно данные нужны. Я там не зря написал про «умеете ли вы ее мерять?».
          +8
          — тесты гарантируют, что если я вот прямо сейчас захочу наговнокодить побыстрому рядышком пару k LOC, то завтра все не упадет у всех

          Тесты убирают страх написания кода в большом и сложном проекте, коим любой проект становится через неделю написания кода тремя человеками

          И у вас нет ресурсов через неделю после начала проекта согласовывать кто и что будет писать в проекте сегодня
          Потому Василию стоит обеспокоится написанием тестов сразу, кроме случаев, когда он пишет сам и сразу в /dev/null
            +2
            Тесты убирают страх написания кода в большом и сложном проекте, коим любой проект становится через неделю написания кода тремя человеками
            А может быть не стоит писать код одновременно тремя человеками неделю? А стоит, эта, разбить его на подзадачи?

            И у вас нет ресурсов через неделю после начала проекта согласовывать кто и что будет писать в проекте сегодня
            Нафига вы вообще посадили на проект, в котором неясно что делать вообще трёх человек?

            Не разделяя идею бессмысленности юнит-тестов в принципе я, всё же, склонен согласиться с тем, что часто их пытаются использовать как замену пониманию: нам не нужно знать что делает этот код — у нас же есть юниттесты!

            Так вот: это работает плохо. Написание кода тремя людьми в шесть рук — приводит код, достаточно быстро, в состояние, когда никакого понятия о том, что этот код делает — нет вообще ни у кого. И после этого его править становится сложно. А править баги — ещё сложнее…
              0
              Как разбивать на подзадачи так, чтоб они никогда не пересекались?

              Как разбивать на подзадачи, так чтоб один разраб всегда отвечал только за тот код, который он написал?
                0
                Как разбивать на подзадачи так, чтоб они никогда не пересекались?
                А кто сказал, что они не должны пересекаться?

                Как разбивать на подзадачи, так чтоб один разраб всегда отвечал только за тот код, который он написал?
                Разраб не должен отвечать «только за тот код, который он написал». Он отвечает за тот код, который он принял.

                А кто его написал — дело десятое. Но у любого компонента должен быть владелец (MAINTAINER или OWNER — в разных проектах по разному), который за него отвечает. И пока он ваш код не «примет» — вы его, в этом компоненте, не увидите.
                  0
                  А кто сказал, что они не должны пересекаться?
                  вы

                  Он отвечает за тот код, который он принял.
                  Как принять 100k LOC?
                    0
                    В таком случае обычно предполагается что на проекте есть условный «тимлид», который сам редко пишет код и занимается в основном интеграциями и планированием.
                    К сожалению, во многих проектах тимлид слишком занят пилением фич и бесполезными митингами, чтобы быть нормальным owner'ом

                    ПС ну и патч на 100k LOC это уж слишком, надо разбивать на подзадачи
                      0
                      И как тимлиду принять в одно лицо столько кода?

                      И еще: как делать рефакторинг без юнит тестов?
                        0
                        Еще раз повторяю — тимлид должен сопровождать изменения на 100к строк, пока эти строки пишутся. Не должно быть ситуации с «неожиданной» кучей кода, которую никто кроме писавшего, не понимает.
                        Ну и никто не говорит что юнит тестов быть не должно. Речь о том что не должно быть хаоса и надежды на юнит тесты, как на единственное спасение.
                          0
                          Что значит
                          Не должно быть ситуации с «неожиданной» кучей кода, которую никто кроме писавшего, не понимает.
                          ?

                          Ето регулярная ситуация при подключении любой либы

                          Речь о том что не должно быть хаоса и надежды на юнит тесты, как на единственное спасение.
                          а на что тогда полагаться?
                            0
                            Ето регулярная ситуация при подключении любой либы
                            А для ядра операционки вы тоже юниттесты пишите? А для Minix'а в ME?

                            Сторонний код — есть всегда. Либо у него есть кто-то, кто за него отвечает (и имеющиеся там сотни тысяч или миллионы строк вас не касаются), либо вы просто его не используете.

                            а на что тогда полагаться?
                            На людей, очевидно. Либо у вас есть люди, знающие что именно они делают, либо нет. В первом случае юниттесты — не так важны, во втором — они вам не помогут.
                              0
                              Сторонний код без тестов используется в крайнем случае

                              Люди допускают ошибки

                              На что полагаться?
                                0
                                Сторонний код без тестов используется в крайнем случае
                                И чего вы этим добиваетесь? Увеличения цены разработки? Есть много других способов.

                                На что полагаться?
                                На людей, однако.

                                Люди допускают ошибки.
                                Люди также исправляют ошибки — на что юниттесты в принципе неспособны.

                                Потому если вас устраивает некоторая вероятность ошибки — вам можно полагаться на людей. Если не устраивает — нужны формальные доказательства, за которые так топит 0xd34df00d.

                                Юнит-тесты же могут лишь являться костылями (хотя, в некоторых случаях полезными, спору нет) в первом случае и не нужны в принципе во втором.
                                  0
                                  Люди допускают ошибки

                                  Да, а тесты пишут тоже люди. Их наличие никак не гарантирует отсутствие ошибок. Более того, их наличие не так уж существенно снижает вероятность ошибок, т.к. в общем случае тестами в первую очередь покрывается тривиальное поведение, которое и без тестов прекрасно проходит валидацию в ручном режиме. А вот сложные кейсы обычно тестами и не покрываются.
                                    0
                                    Те вместо написания кода разработчики будут делать ручное тестирование при каждом изменение кода?

                                    А самые дорогостоящие разработчики еще и будут заниматься ручным тестирование чужого кода?

                                    Я наконец-то понял вашу позицию
                                      +3
                                      Те вместо написания кода разработчики будут делать ручное тестирование при каждом изменение кода?

                                      А вам не приходило в голову, что в 99% проектов не нужно делать тестирование всего и вся при каждом изменении кода? Что нормально написанный код не приводит к тому, что изменение функции А ломает функции С, D и Z? И что с другой стороны, если вы меняете алгоритм функции А, то вы должны и тест под неё переписать, а значит, дорогостоящий разработчик будет делать куда больше рутинной работы, чем просто взять и раз эту функцию прогнать вручную.
                                      И что ошибка в нижележащем коде обычно находится сразу по результатам UI-теста, без всяких юнит-тестов, вы тоже не слыхали?
                                      А самые дорогостоящие разработчики еще и будут заниматься ручным тестирование чужого кода?

                                      Вы издеваетесь? Зачем тестировать чужой код вообще? У вас есть библиотека, есть отзывы о её применении. Что вы там тестировать собрались? Опыт её эксплуатации — единственный более-менее достоверный тест. Чужие тесты вам никакой гарантии не дадут, а собственные — дурацкая трата времени и денег.
                        0
                        Как принять 100k LOC?
                        Попросить того, кто «это» сотворил разбить на кусочки в 100-200 строк и в каждом из них описать — что он делает и зачем.
                          0
                          Хм
                          Как попросить гугл (или любую корпорацию) сделать для их либ
                          разбить на кусочки в 100-200 строк и в каждом из них описать — что он делает и зачем
                          ?
                            0
                            Я прошу прощения, а вы, подключая либы от сторонних вендоров, делаете и повторно работу за этих вендоров — рефакторите их код, обвешиваете тестами?
                              0
                              Не подключаю если там нет нормального покрытия тестами
                                0
                                Это, честно говоря, больше похоже на некий фанатизм, чем на осознанное решение.
                                  –1
                                  Нет
                                    0
                                    Да. Выходит, что здравые основания по выбору библиотеки, т.е. её полезность для вашего проекта, её известность и распространённость могут быть перевешены второстепенным фактором, представляющим собой лишь иллюзию надежности. Ведь вы, с одной стороны, понятия не имеете, насколько тесты написаны качественно, как они покрывают код. Тесты нашлёпаны, формальности соблюдены — можно брать. С другой стороны, отсекаете массу качественных и хороших продуктов, разработчики которых не пишут тесты.
                                      0
                                      А чем может быть полезна дырявая библиотека?

                                      И опять же.
                                      Вы так топите за ручную проверку кода людьми и при етом не допускаете, что тесты также можно просмотреть и оценить?

                                      Странно, странно
                                        +2
                                        Вы так топите за ручную проверку кода людьми и при етом не допускаете, что тесты также можно просмотреть и оценить?

                                        Я не допускаю, что адекватный, а не пустивший всё на самотёк, заказчик софта будет готов платить деньги команде, которая тратит их на подобную хрень. Давайте ещё тестеров для тестеров нанимать. Чего уж там, раз решили подробно выверять юнит-тесты сторонних библиотек, то вполне логичное продолжение.
                  +5
                  тесты гарантируют, что если я вот прямо сейчас захочу наговнокодить побыстрому рядышком пару k LOC, то завтра все не упадет у всех

                  Нет, они этого не гарантируют ни формально, ни эмпирически.


                  Тесты убирают страх написания кода в большом и сложном проекте, коим любой проект становится через неделю написания кода тремя человеками

                  И снова далеко не обязательно.


                  То есть, понятно, что сферические правильно написанные правильной командой в правильном вакууме тесты действительно помогают, но это совсем другой разговор, и при таких вводных можно рассмотреть и другие способы обеспечения надёжности.

                    0
                    Нет, они этого не гарантируют ни формально, ни эмпирически.
                    почему проверка функционирования кода не гарантирует его работоспособность?

                    при таких вводных можно рассмотреть и другие способы обеспечения надёжности.
                    какие?
                      +1
                      почему проверка функционирования кода не гарантирует его работоспособность?

                      Потому что проверяются только конкретные частные случаи. И формальных доказательств достаточности покрытия тестов я не видел ни разу.


                      какие?

                      То же использование систем формальной верификации.

                        0
                        Потому что проверяются только конкретные частные случаи. .
                        да, проверяется как код должен работать.
                        Так как тест, проверяющий корректную работу кода не гарантирует, что код будет работать так как он задуман?

                        То же использование систем формальной верификации.
                        Что сие есть?
                        Как оно делается?
                        Как оно гарантирует корректность?
                          0
                          Так как тест, проверяющий корректную работу кода не гарантирует, что код будет работать так как он задуман?

                          Он не проверяет, что код будет работать так, как нужно, на всём возможном пространстве входов.


                          Даже если вы проверили, что sort [1, 3, 2] == [1, 2, 3], то вы всё равно упустили случаи вроде


                          1. пустого списка,
                          2. списка с повторяющимися элементами,
                          3. списка с элементами, на которых нет линейного порядка (IEEE754, например),
                          4. списка не из чисел.

                          Что сие есть?

                          Это вещи от всяких TLA+ (коих я большой не фанат) до языков типа Idris (коих я, наоборот, большой фанат) или Coq.


                          Как оно делается?

                          Зависит от конкретного инструмента. В случае того же идриса или кока — пишется код, носящий с собой доказательство корректности в своих типах.


                          Как оно гарантирует корректность?

                          Математически.

                            –3
                            Он не проверяет, что код будет работать так, как нужно, на всём возможном пространстве входов
                            что не является задачей юнит тестов, а задачей регресионных тестов

                            Даже если вы проверили, что
                            такие юнит-тесты никто не пишет

                            Зависит от конкретного инструмента. В случае того же идриса или кока — пишется код, носящий с собой доказательство корректности в своих типах.
                            те написание тестов с помощью того инструмента, который вам нравится, ок
                            То что не нравится неок

                            Хммм
                              +1
                              что не является задачей юнит тестов, а задачей регресионных тестов

                              Не понял. А зачем тогда юнит-тесты нужны?


                              такие юнит-тесты никто не пишет

                              Это упрощённый пример, призванный проиллюстрировать, почему наличие тестов ничего не гарантирует.


                              Как вы напишете тесты на функцию сортировки? На какие-нибудь деревья?


                              те написание тестов с помощью того инструмента, который вам нравится

                              Нет, это не написание теста. В тесте вы принципиально не можете использовать квантор всеобщности, в тесте вы проверяете только квантор существования.

                                0
                                Не понял. А зачем тогда юнит-тесты нужны?

                                да, проверяется как код должен работать.


                                Как вы напишете тесты на функцию сортировки? На какие-нибудь деревья?
                                а зачем тестировать, то что уже покрыто тестами?

                                В тесте вы принципиально не можете использовать квантор всеобщности
                                потому что в реальности у вас нет возможности пройтись по всем значениям в любом случае

                                Кстати, кто и где использует ваши инструменты для покрытия тестами?
                                  +1
                                  да, проверяется как код должен работать.

                                  Он не проверяет, что код будет работать так, как нужно, на всём возможном пространстве входов

                                  Так что он проверяет-то?


                                  а зачем тестировать, то что уже покрыто тестами?

                                  Мне казалось очевидным, что разговор о той функции сортировки или какому-нибудь алгоритму на графе, который вы сами только что написали.


                                  потому что в реальности у вас нет возможности пройтись по всем значениям в любом случае

                                  Именно!


                                  Но по всем значениям и не надо проходить, если вы используете упомянутые средства формальной верификации. Вы же в математике когда теоремы со значком ∀ доказываете, вы не проверяете вручную все интересующее вас множество, по которому он квантифицирует?


                                  Кстати, кто и где использует ваши инструменты для покрытия тестами?

                                  Для покрытия тестами — нигде, потому что это не тесты.

                                    0
                                    Те опять вкусовщина — один инструмент у вас дает гарантии, другой нет

                                    Кстати про математику — что там с автоматическим доказательством теорем?
                                      +1
                                      Те опять вкусовщина — один инструмент у вас дает гарантии, другой нет

                                      Ага, математические гарантии. Непонятно, правда, почему вкусовщина, если дело не в конкретных инструментах, а в классах инструментов.


                                      Вкусовщиной бы было, если бы я тут топил за Idris или Agda и против Coq, ибо стили доказательств в идрисне мне больше во вкусу, чем (ИМХО) нечитаемые proof script'ы из вызовов тактик в Coq.


                                      Кстати про математику — что там с автоматическим доказательством теорем?

                                      Неразрешимо в смысле Тьюринга. Что не мешает иметь разрешимый тайпчекинг для достаточно мощных систем типов, что даёт автоматические проверки доказательств теорем.


                                      И полезные эвристики, дающие доказательство для ряда практически полезных случаев, тоже не мешает иметь.

                                        0
                                        Не в курсе про автопроверку доказательств
                                        Подскажите материалы по данному поводу?
                                          0

                                          Смотря что конкретно вы хотите узнать.


                                          Если говорить о метатеории, то доказательство разрешимости тайпчекинга в Calculus of Constructions (и Calculus of Inductive Constructions), который позволяет формализовать (почти) всю математику, и разрешимость которого является основанием для заявлений об автопроверке доказательств, есть, но конкретные ссылки мне придётся поискать.


                                          О том, как это вообще выглядит и работает, можно почитать на википедии про всякие пруф ассистанты, или в книге Type theory and Formal Proof (хорошее введение для начинающих без серьёзных метатеоретических изысканий), или в книге Certified programming with dependent types, или в книге Type-driven development with Idris.

                                            0
                                            Мне нужна статья про
                                            даёт автоматические проверки доказательств теорем
                                              0

                                              Вторая глава книги «Advanced Topics in Types and Programming Languages» целиком про доказательство разрешимости, рекомендую начать с неё, плюс там неплохая библиография. Плюс вот это. Чтобы понять, какое отношение вся эта ерунда имеет к математике, можно начать с упомянутой выше книги Type Theory and Formal Proof либо даже со статьи в вики, если книга — слишком сложно. Если ATTAPL тоже сложно, то можно открыть другую статью на вики про CoC, увидеть там строчку «The CoC is strongly normalizing» и сделать выводы.


                                              Надеюсь, это вас устроит.

                                                0
                                                Вполне устроит
                                                Спасибо

                                                The CoC is strongly normalizing


                                                although, by Gödel's incompleteness theorem, it is impossible to prove this property within the CoC since it implies inconsistency.


                                                  0

                                                  На практике проблем это не приносит.


                                                  Да и если вас это смущает, то вас вообще должна смущать вся математика — там при анализе всяких логических метатеорий вылезают похожие проблемы.

                                                    0
                                                    Меня больше смутил простой пример
                                                    The proof of commutativity of addition on natural numbers
                                                      0

                                                      Там какая-то ерунда понаписана. Я не так давно развлекался с числами, можно ИМХО проще и яснее:


                                                      plusRightZero : (n : Nat) -> n = n + Z
                                                      plusRightZero Z = Refl
                                                      plusRightZero (S n) = cong $ plusRightZero n
                                                      
                                                      plusRightS : (n, m : Nat) -> n + S m = S (n + m)
                                                      plusRightS Z m = Refl
                                                      plusRightS (S n) m = cong $ plusRightS n m
                                                      
                                                      plusCommutes : (n, m : Nat) -> n + m = m + n
                                                      plusCommutes Z m = plusRightZero m
                                                      plusCommutes (S n) m = rewrite plusCommutes n m in sym $ plusRightS m n

                                                      Здесь аж три теоремки, последняя опирается на две предыдущих, как всегда в математике. Так что само доказательство искомой теоремы занимает буквально две строки.

                                +1

                                Юнит-тесты, ставшие однажды зелёными, становятся регрессионными тестами. Это их основная задача обычно. Иначе нет смысла хранить их в кодовой базе.

                      0
                      — тесты гарантируют, что если я вот прямо сейчас захочу наговнокодить побыстрому рядышком пару k LOC, то завтра все не упадет у всех

                      Тесты гарантируют лишь то, что если вы захотите наговнокодить пару kLOC, по-быстрому вы это сделать уже никак не сможете :)
                      И они как-то снизят возможность падения, в зависимости от своего качества. Причем тесты, сделанные для говнокода по той же методике разработки, и работать будут так же.
                      Изначальный посыл статьи верный: тесты должны быть лишь в том случае, если их разработка дешевле, чем ожидаемые последствия от поиска багов.
                        0
                        Тесты гарантируют лишь то, что если вы захотите наговнокодить пару kLOC, по-быстрому вы это сделать уже никак не сможете :)
                        почему нет?

                        Изначальный посыл статьи верный
                        Я не знаю изначальный замысел статьи
                        Как я вижу, статья пытается, исходя из вымышленных препосылок, что-то утверждать
                          0
                          почему нет?

                          Да потому что вы тесты писать будете. Они же не сами сверху падают, как манна небесная. Это ваше рабочее время. Вы можете реализовывать фичи, а можете писать тесты.
                          Как я вижу, статья пытается, исходя из вымышленных препосылок, что-то утверждать

                          Посыл статьи — сначала считайте, нужны вам тесты или нет. И есть достаточно высокая вероятность, что арифметика покажет, что не нужны. Ничего хитрого, всё очевидно. Так оно и есть, экономически для множества (и пожалуй, я не ошибусь, если скажу, что даже для большинства) проектов тесты не выгодны, и их создание не окупается никогда.
                            0
                            Вы можете реализовывать фичи, а можете писать тесты.
                            А кто подтвержит, что фича написана правильно?

                            Посыл статьи — сначала считайте, нужны вам тесты или нет.
                            в статье нет основы для таких расчетов. Потому как посчитать — также не ясно
                            С економической точки зрения дешевле передать всю разработку на аутсорс в Индию
                              +1
                              А кто подтвержит, что фича написана правильно?

                              В 90% проектов с этим успешно справляется команда тестировщиков. Схема «провели UI-тесты и подтвердили, что всё нижележащее отработало согласно ТЗ» вполне жизнеспособна, и за исключением ряда специфических продуктов обеспечивает достаточный уровень качества.
                              в статье нет основы для таких расчетов. Потому как посчитать — также не ясно

                              Эмпирическое соотношение давно известно и не является секретом. Умножайте трудозатраты на разработку ровно в два раза, и получите примерный эстимейт для кода, хорошо покрытого тестами.
                                –1
                                Давайте отставим функциональные тесты в сторону — будем считать, что они есть, покрывают все ТЗ и ручное тестирование сведено к абсолютному минимуму

                                И нет
                                Емпирика также никого не интересует в расчетах, где есть деньги

                                Я уже не говорю про то, что трудозатраты посчитать крайне сложно

                                И вернемся к основному вопросу
                                Как делать частичный рефакторинг, когда у вас есть только функциональные тесты?
                                  +2
                                  И нет
                                  Емпирика также никого не интересует в расчетах, где есть деньги

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

                                  Я не понимаю смысла этого вопроса. Берете и делаете. Вам тесты для выполнения рефакторинга не нужны. Они играют лишь вспомогательную роль, упрощая локализацию возможных ошибок при рефакторинге. У вас есть какие-то сомнения, что получится провести успешный рефакторинг без автоматизированной локализации возможных ошибок?
                                    –1
                                    Как берете и делаете?
                                    Конкретнее
                                      0
                                      Так некуда уже конкретнее. Возьмите любой известный вам пример рефакторинга, это именно то, что я имею в виду.
                                        –3
                                        Чем дальше, тем меньше конкретики
                                        Жаль
                      +4
                      но я видел с десяток наверное статей прямо тут, где ультимативно утверждается: «Вы обязаны писать тесты».

                      Большинство тех кто пишет такие статьи не покупает разработку, а получает с неё деньги
                        +1
                        Да, разумеется. Но заметьте, даже в этом случае, будучи на стороне разработки, я бы все равно подумал, на что мне стоит потратить свое время и чужие деньги — на написание тестов, или на новую функциональность.
                          0

                          По идее не разработчик должен об этом думать, а его менеджер. Качество или скорость выбрать и донести свой выбор до разработчиков.

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

                              Наверное, если заказчик платит за тесты, то надежность и качество для него не на последнем месте.

                                0
                                Я бы на его месте платил за показатели, которые можно померять. А уж как разработчики их достигли — зачем заказчику в это вникать?
                                  0

                                  Число строк/методов/классов/файлов? Отработанные часы? :)


                                  Принесённый доход/прибыль?

                                  0
                                  Не обязательно, кстати. Могут быть и другие варианты:
                                  — Заказчик не слишком компетентный в особенностях разработки, и ему просто продали разработку тестов.
                                  — Заказчик не знает, что он платит за тесты. Стоимость их разработки просто тихой сапой включена в общий эстимейт.
                                  И то, и другое встречается нередко.
                        +11
                        Хорошая статья, показывающая что есть ложь, есть наглая ложь, а есть статистика.

                        Рассмотрим первый случай. Вероятность сбоя возьмем равной 33%, то есть инцидент, способный завалить их сайт надолго может произойти примерно раз в три года, что не так уж плохо.

                        Один инцидент раз в три года? Ну это примерно частота, с которой падают такие сервисы, как GMail или YouTube. Сервисы типа Steam или там Deutsche Bank — падают в разы чаще. И, разумеется, все эти компании вбухивают хорошие деньги в свои сервисы и в юниттестирование.

                        Сказать, что вы получаете вероятность сбоя за год 33% (без юниттестирование, напоминаю) — это примерно как рассмотреть вариант «мы наняли 500 водителей-такжиков по объявлению, ну а они, конечно, без тренировок и дополнительных затрат ездят чуть получше, чем Шумахер… ну… большинство из них».

                        То есть посыл статьи прост: если вы наняли супергениев, которые, по сравнению со средними программистами Гугла и Яндекса — как Эйнштейн по сравнению с трёхлетним ребёнком (работники Гугла — тут трёхлетним дети, это если вдруг, кто не понял)… то да, вам, видимо, юниттестирование не нужно.

                        Вот только… где вы берёте супергениев, которые без всяких тестов дают такую же надёжность, как у лучших компаний в мире со всякими многоуровневыми тестами, SRE и прочим… секретом не поделитесь?

                        Без такого «секретного места» ценность статьи, извините, близка у нулю.
                          +1
                          Ну статья всего лишь о том, что перед тем как что-то делать стоит это попытаться просчитать… как мне кажеться — это разьяснение программистам как мыслят их заказчики и директора…
                            +6
                            Ну если их «заказчики и деректора» верят в то, что можно достичь уровня «пяти девяток» (а час в три года — это очень близко) без очень существенных затрат — то дальнейшие разговоры будут весьма непростыми. И вообще неясно — стоит ли их вести.
                              0
                              без очень существенных затрат

                              Нормальные — не верят.Но таки хотят цифр, выраженных в денежных знаках…
                                +4
                                Кстати, почему вы думаете, что youtube падает раз в три года? Скажем для меня, как для пользователя, его надежность выглядит совсем не так — примерно раз в неделю я наблюдаю, как отдельные видео не грузятся, и вместо них лишь сообщение об ошибке на черном фоне. Так что я бы не преувеличивал надежность гугловских сервисов.
                                  –1
                                  Так что я бы не преувеличивал надежность гугловских сервисов.
                                  Вот только по статистике — они надёжнее Netflix'а и многих других.

                                  Кстати, почему вы думаете, что youtube падает раз в три года?
                                  Можете посмотреть хотя бы по газетным заголовкам.

                                  Скажем для меня, как для пользователя, его надежность выглядит совсем не так — примерно раз в неделю я наблюдаю, как отдельные видео не грузятся, и вместо них лишь сообщение об ошибке на черном фоне.
                                  Ну это вообще для подобных сервисов норма. Речь не идёт о выпадании чего-то для отдельных посетителей же в вашем сообщении. А о полном падении сервиса.
                                    0
                                    Можете посмотреть хотя бы по газетным заголовкам.

                                    Когда раз в два дня какое-нибудь видео зависает — в газетах о таком рядовом событии не пишут.

                                      0
                                      Если бы вы читали статью, то заметили бы, что речь идёт не о «зависающих видео, а о, цитирую:
                                      инцидент, способный завалить их сайт надолго
                                      К YouTube применяем те же критерии.
                                    0

                                    Вот, да. Безотносительно темы статьи, в YouTube полно различных мелких глюков. Некоторые из которых можно наблюдать месяцами, если не годами.


                                    С другой стороны, это не такое уж и критичное к ошибкам приложение.

                                0
                                Обратите внимание, в примере речь идет о крупной компании с выручкой 25 — 50 миллиардов. Разумеется, у нее есть и тестировщики и грамотная стратегия для деплоя и автоматизированные тесты помимо юнит тестов.

                                Кроме того, ничто не мешает вам попробовать подставить в модель свои данные и это здорово, потому что мы уже переходим с уровня верю — не верю к конкретным показателям.
                                  0
                                  Разумеется, у нее есть и тестировщики и грамотная стратегия для деплоя и автоматизированные тесты помимо юнит тестов.
                                  В таком случае нужно сравнивать не с затратами времени на юниттесты, а с затратами времени на другие виды тестов. Если у вас 30% времени тратится на униттесты и ещё 20% на функциональные, но функциональные покрывают 90% проблем, а юниттесты 99% — то эффект от юниттестов, тем не менее, гораздо выше, чем от функциональных. И лучше убрать как раз функциональные, которые, вроде как, дешевле.

                                  Писать нужно те тесты, которые больше подходят для компонента, который разрабатываете.
                                    +2

                                    В сложных системах код отдельных фрагментов, которые могут быть покрыты юнит-тестами, местами достаточно примитивен. Поэтому юнит-тесты вырождаются до набора моков, проверяющих, что "А дёргает Б, после чего дергается В и Г" (я эту мысль пытался раскрыть в "Unit-тестирование в сложных приложениях"). Так что лучше как-раз таки оставить функциональные тесты, т.к. юнит-тесты не выявляют имеющихся проблем, а только свидетельствуют о том, что проверяемый фрагмент кода работает так, как думает, что так и должен работать этот фрагмент, программист, его написавший (это особенно характерно для TDD). Т.е., 99% покрытие кода юнит-тестами не говорит о том, что код работает правильно, а всего лишь о том, что тесты обнаружат изменения в покрытом коде, если он будет изменён без соответствующего изменения тестов.


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

                                      0
                                      А как делать рефакторинг без юнит-тестов?
                                        0

                                        Изменяете код и проверяете, что он отрабатывает так, как вы ожидаете. Если в вашем проекте вы не можете делать это без юнит-тестов, то рефакторьте с юнит-тестами. Если можете проверить без юнит-тестов — рефакторьте без.

                                          0
                                          Вы же понимает, что ето не ответ?
                                            +3

                                            Вы же понимаете, что до появления юнит-тестов рефакторинг как-то делали?

                                              0
                                              Как?
                                                0

                                                Проверяя работоспособность программы вручную, разумеется.


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

                                                  0
                                                  Сочувствую
                                                    +1

                                                    Сочувствие требовалось бы, если бы так и продолжили поддерживать тот проект без рефакторинга...

                                                  0

                                                  В этой статье написано:


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

                                                  По мере разрастания проекта, времени на ручное тестирование будет оставаться все меньше и меньше…

                                                  Отсюда логически вытекает необходимость автоматизировать работу тестировщиков…

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

                                                  а затем змея начинает кусать свой хвост:


                                                  На практике чаще всего написанием юнит-тестов для собственного кода занимается сам разработчик.

                                                  Т.е., мы приходим к тому, от чего уходили — разработчик пишет проверочные тесты для того, что сам разрабатывает. Причём не проверку совмещения отдельных частей в единое целое (интеграционные), а проверяет каждую букву своей пьесы (юнит). При этом кол-во проверок равно кол-ву букв, а размерность алфавита не ограничена. Даже если разработчик неверно написал букву, он не сможет проверить её неправильность тестом, т.к. и тест он напишет неправильно. Он просто неправильно представляет себе, как это должно работать.


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


                                                  В статье пропущен шаг функционального тестирования, а я считаю, что это — минимальный уровень, на который есть смысл опускаться. Если есть функциональные тесты и выше, то юнит-тесты просто не нужны. Зачем проверять, как работают отдельные буквы, если получаются нужные слова, из которых выстраиваются правильные предложения?


                                                  До появления юнит-тестов рефакторинг делали невзирая на наличие тестов, довольствуясь ручной проверкой, затем проверку стали автоматизировать, затем появились функциональные и интеграционные тесты, а потом решили получить 100% code coverage и вышли на unit-тесты.


                                                  Простой вопрос, считаете ли вы, что полноценное юнит-тестирование должно на 100% покрывать код проекта?


                                                  Если вы не гонитесь за 100% code coverage unit-тестами, то мы с вами, скорее всего, имеем в виду одно и то же под разными названиями (вы называете это юнит-тестами, я — функциональными тестами).

                                                    0

                                                    Проблемы высокоуровневых тестов основных три:


                                                    • сложно задать нужное состояние системы, чтобы протестировать конкретное управляющее воздействие
                                                    • сложно проверить, что у системы нужное состояние после тестируемого управляющего воздействия
                                                    • они медленные
                                                      0

                                                      Тем не менее, они что-то проверяют, в отличие от. А что проверяют юнит-тесты?

                                                        0

                                                        Юз-кейсы конкретного модуля.

                                                          0

                                                          Модуль в данном контексте это что — функция, класс, скрипт, набор скриптов? Как задаётся нужное состояние модуля, чтобы протестировать конкретное управляющее воздействие?

                                                            0

                                                            В зависимости от языка и архитектура понятия модуля могут сильно меняться. Насколько я знаю общая практика для ООП-языков: модуль — класс, тестируется вызов одного метода. Создаём инстанс класса, приводим его к нужному состоянию различными способами, вызываем метод, проверяем состояние.

                                                              0

                                                              Возможно ли функциональное тестирование отдельного класса? Если да, то чем функциональное тестирование отдельного класса отличается от юнит-тестирования отдельного класса?

                                                                0

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

                                                                  0

                                                                  Вот я и считаю, что достаточно функционального тестирования для проверки работоспособности модуля/приложения. Юнит-тестирование избыточно. Оно создаёт ложное ощущение контроля ситуации, не предоставляя таковой по сути. Нам не нужно проверять всё, нам нужно проверять только то, что действительно важно, на что есть спецификация (требования).

                                                                    0

                                                                    Юнит-тесты по сути формализованные требования к модулю. Нам не нужно проверять, что модуль соответствует требованиям к модулю?

                                                                      0

                                                                      Кто формализует требования к модулю в виде юнит-тестов? Разработчик, который пишет сам модуль? TDD подразумевает, что да. Если разработчик неправильно понимает, что должен делать его модуль, он создаст неправильный тест для которого напишет неправильный код. В результате, неправильный юнит-тест отработает правильно и покажет, что неправильный код работает, как и задумывалось. При рефакторинге придётся переделывать и код, и юнит-тест к нему.


                                                                      Вы выше написали, что юнит-тестирование — подвид функционального. Если требования к модулю, которые проверяет юнит-тест, пишутся не самим программистом в качестве среды для разработки кода модуля, а являются следствием преобразованния функциональных требований, такое юнит-тестирование (как подвид функционального) я считаю правильным и достойным существования. Как только юнит-тестирование превращается в создание среды для разработки отдельного модуля, то тут уже начинаются фантазии разработчика.


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


                                                                      Я сказал, что мог, и больше не могу ничего добавить к.

                                                                        +1

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

                                                        0

                                                        Это всё проблемы не тестов, а архитектуры. Например, высокоуровневый тест берущий ToDoMVC и проверяющий, что если добавить 2 задачи, одну завершить, а потом потыкать по фильтрам, то список задач будет правильно перефильтровываться:


                                                        // Создали инстанс компонента в тестовом контексте
                                                        const app = $mol_app_todomvc.make({ $ })
                                                        
                                                        // Ввели текст в инпут добавления задачи
                                                        app.Add().value( 'test title' )
                                                        // Закоммитили добавление задачи
                                                        app.Add().event_done()
                                                        
                                                        // Добавили ещё одну задачу
                                                        app.Add().value( 'test title 2' )
                                                        app.Add().event_done()
                                                        
                                                        // Щёлкнули по чекбоксу завершения задачи
                                                        app.Task_row(1).Complete().event_click()
                                                        
                                                        // Проверили, что изначально показываются обе задачи
                                                        $mol_assert_like( app.List().sub() , [ app.Task_row(1) , app.Task_row(2) ] )
                                                        
                                                        // Перешли по ссылке "только завершённые"
                                                        app.$.$mol_state_arg.href( app.Filter_completed().uri() )
                                                        $mol_assert_like( app.List().sub() , [ app.Task_row(1) ] )
                                                        
                                                        // Перешли по ссылке "только активные"
                                                        app.$.$mol_state_arg.href( app.Filter_active().uri() )
                                                        $mol_assert_like( app.List().sub() , [ app.Task_row(2) ] )
                                                        
                                                        // Перешли по ссылке "все задачи"
                                                        app.$.$mol_state_arg.href( app.Filter_all().uri() )
                                                        $mol_assert_like( app.List().sub() , [ app.Task_row(1) , app.Task_row(2) ] )

                                                        Исполняется всего 3 миллисекунды.

                                                          0

                                                          Это вместе с ответом сервера? :)

                                                            0

                                                            Это на мокнутом сервере.

                                                              +1

                                                              Создание моков вы не учитываете?

                                                                0

                                                                Они создаются один раз и используются всеми тестами. Вот, например, мок локального хранилища который используется в ToDoMVC.

                                                                  0

                                                                  Пускай, тем не менее юнит-тесты на условной модели отработают быстрее чем на полном приложении.


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

                                                                    +1

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


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

                                                                      0

                                                                      DRY при написании юнит-тестов никто не отменял. Как и оптимизацию по скорости путём переиспользования (главное, чтобы изолированность тестов не нарушалась).


                                                                      Юнит-тесты скажут в какой функции ошибка :) При хороших подходах к коду и тестам.


                                                                      Ещё один минус высокоуровневых — не понятно кто и когда их должен писать, если несколько человек пилят одну фичу, ну там один UI, другой модель/стор, третий и/о сервисы и т. п.

                                                                        0

                                                                        Каждый свою часть тестирует.

                                                                          0

                                                                          Мокая остальные? Чем тогда это отличается от юнит-тестов? :)

                                                                            0

                                                                            Не мокая остальные.

                                                                              0

                                                                              Как UI разработчик будет тестировать, если стор ещё не готов, не мокая его?

                                                                                0

                                                                                Для этого есть MockDrivenDevelopment. Сначала делаем мок, чтобы зависимый код можно было разрабатывать. И не спеша пишем нормальную реализацию, заменяя моки.

                                                                                  +2

                                                                                  Когда-то такой подход назывался "нисходящее программирование"...

                                                                                    0

                                                                                    "нисходящее" имеет негативные коннотации и звучит как какой-то бородатый легаси. А вот WhateverDriverDevelopment звучит как best pactice.

                                                                                    0

                                                                                    Ну примерно так и выглядит юнит тестирование. Замокали все зависимости и тестируем один модуль.

                                                                                      0

                                                                                      Вы доказать-то что пытаетесь?

                                                  0

                                                  Ну, собственно, для того тесты и нужны чтобы проверять, что код отрабатывает так, как ожидается. Конкретно юнит-тесты — обычно самый быстрый способ проверки. То есть наоборот, если не можете проверить с юнит-тестами, то тогда придётся как-то проверять без них. Вплоть до выкатки на продакшен и ожидания багов.

                                                  0

                                                  Отлично!


                                                  Каждый раз, когда я рефакторю код на хаскеле, интеграционные тесты сразу зелёные после того, как код протайпчекался.

                                                    –1
                                                    А тем кто не пишет на функциональных языках или языках со статической типизацией?
                                                      +1

                                                      Не знаю, я стараюсь минимизировать взаимодействие с языками без строгой статической типизации.

                                                        –1
                                                        Ну вот
                                                          +1

                                                          Что «ну вот»? Вы спросили, как можно делать рефакторинг без юнит-тестов, я ответил, как можно делать рефакторинг без юнит-тестов.

                                                            –1
                                                            Как?
                                                              +1

                                                              Берёте и делаете. Потом исправляете ругань тайпчекера, как исправили — считайте, сделали. Можно? Можно.


                                                              Вот как юнит-тесты помогают делать рефакторинг, мне больше непонятно, ибо сами эти юниты (как нас учит википедия, нетривиальные функции) же меняются, значит, надо обновлять тесты.

                                                                –1
                                                                Юнит-тесты должны быть тривиальны
                                                                Тесты пишутся перед изменением функционала

                                                                По сути наш разговор свелся к тому, что, по вашим словам, стабильную разработку на ЯП кроме особенных вести нельзя.

                                                                И как бы мне не хотелось все писать на кложуре, пока не получается
                                                                  +1
                                                                  Юнит-тесты должны быть тривиальны

                                                                  Что это значит?


                                                                  Тесты пишутся перед изменением функционала

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


                                                                  По сути наш разговор свелся к тому, что, по вашим словам, стабильную разработку на ЯП кроме особенных вести нельзя.

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


                                                                  И как бы мне не хотелось все писать на кложуре, пока не получается

                                                                  Clojure тут ну совсем никак не поможет, но неважно.

                                                                    –2
                                                                    Вы совсем не знакомы с тдд воркфлоу?

                                                                    Тогда и хаскель никак вам не помогает или вы запутались?
                                                                      0
                                                                      Вы совсем не знакомы с тдд воркфлоу?

                                                                      Type-driven development? Знаком.


                                                                      А если чуть серьёзнее, то и в случае TDD я представляю, что люди делают.


                                                                      Но давайте обсудим на примере. Вот пишете вы сами лично функцию сортировки, скажем. Timsort там какой-нибудь. Какие тесты вы на неё пишете?


                                                                      Тогда и хаскель никак вам не помогает или вы запутались?

                                                                      Помогает чуть меньше, чем другие языки, ибо избавляет меня от более узкого класса ошибок. Даже со всеми современными GADTs, DataKinds, TypeFamilies и прочим этим вот всем.

                                                                        0
                                                                        Но давайте обсудим на примере. Вот пишете вы сами лично функцию сортировки, скажем. Timsort там какой-нибудь. Какие тесты вы на неё пишете?

                                                                        1. краевые случаи: пустой список, список из одного элемента, упорядоченный список, обратно упорядоченный список, список из одинаковых элементов;


                                                                        2. некоторое количество случайных перестановок;


                                                                        3. некоторое количество случайных списков с повторяющимися элементами;


                                                                        4. большой список (для проверки быстродействия);


                                                                        5. несколько тестов с некорректными компараторами, чтобы проверить отсутствие повреждений памяти, если язык этого не гарантирует.


                                                                          +1
                                                                          И вот представьте себе, вы предложили сделать пять автоматических тестов для одной функции, которая в общем-то пишется раз и навсегда, без каких-либо изменений. При этом тесты, судя по всему, в несколько раз будут превышать сложность самой функции. Надо ли это делать вообще?
                                                                            0

                                                                            А вы что, предлагаете вообще ни разу не запускать написанную функцию?


                                                                            Ну хорошо, на том же Идрисе это возможно, но там на построение строгого доказательства работы как бы не больше времени потрачено будет. А что делать на "обычных" языках программирования?

                                                                              0
                                                                              Да не предлагаю, конечно. Но написание теста как минимум подразумевает, что есть потребность проверять этот код многократно. Я бы просто проверил работоспособность функции, а остальное оставил бы на откуп функциональным/интеграционным тестам. Если там где-то вылезет проблема с отображением данных, например, по производительности/потреблению памяти, тогда бы уже погружался в детали. Я не сторонник делать значительный объем работы просто так, «на всякий случай», вдруг оно там понадобится. Потому что опыт показывает, что практически никогда не понадобится, а в тех редких случаях, когда понадобится, можно доделать потом. Запустить проект раньше и усовершенствовать его, когда он уже работает и приносит деньги, это выгоднее, чем делать его сразу правильно, но запускать позже (естественно, при условии, что вы запускаете его в работоспособном виде, а не совсем кривую поделку). Это касается и избыточной заложенной в проект масштабируемости, и избыточных тестов, и т.п.
                                                                              И естественно, вышенаписанное — это общий случай. Если эта функция сортировки будет являться краеугольным камнем в моём приложении, то и подход к ней будет иной.
                                                                                0
                                                                                Но написание теста как минимум подразумевает, что есть потребность проверять этот код многократно.

                                                                                Так ведь в процессе отладки функции её именно что нужно проверять многократно.


                                                                                Я бы просто проверил работоспособность функции

                                                                                Так для того тесты и пишутся


                                                                                Я не сторонник делать значительный объем работы просто так

                                                                                В чем заключается значительный объём работы? В том, чтобы написать один класс и десяток заголовков методов? Содержимое тестов сюда точно не входит: его придется писать в любом случае, функцию же проверить надо, пусть даже и однократно (ха-ха).

                                                                                  +1
                                                                                  В чем заключается значительный объём работы? В том, чтобы написать один класс и десяток заголовков методов?

                                                                                  Так не только заголовки ведь. Вам нужно подготовить тестовые наборы данных для каждого случая, входные и выходные для сравнения, продумать, какие assert'ы нужны, прописать их. Проверить всё это, т.к. тесты — такой же самый код, и там тоже могут быть ошибки. Это всё такая же работа, как и написание самой функции, причем достаточно объемная.
                                                                                  Я бы просто проверил работоспособность функции

                                                                                  Я имею в виду, в целом, а не обкладывать её тестами с краеугольными случаями со всех сторон.
                                                                                    0
                                                                                    Вам нужно подготовить тестовые наборы данных для каждого случая

                                                                                    А при проверке работоспособности функции это все, как бы, не нужно?


                                                                                    и выходные для сравнения

                                                                                    Не обязательно. При правильном построении исходных данных корректность сортировки можно проверить без "эталонных" выходных.


                                                                                    Тесты в пунктах 4 и 5 можно оставить и вовсе без проверки результата: не падает и ладно.


                                                                                    Проверить всё это, т.к. тесты — такой же самый код, и там тоже могут быть ошибки.

                                                                                    Отдельно тесты проверять не нужно. Во-первых, тесты должны быть значительно проще проверяемого алгоритма — тогда и ошибок в них будет заведомо меньше. Во-вторых, первый же запуск тестов проверит заодно и их.


                                                                                    Я имею в виду, в целом, а не обкладывать её тестами с краеугольными случаями со всех сторон.

                                                                                    А кому нужна функция сортировки, работающая "в целом"?

                                                                                      0
                                                                                      А при проверке работоспособности функции это все, как бы, не нужно?

                                                                                      Нет, я же делаю не сферическую функцию в вакууме. В приложении у меня будет набор входных данных там, где она будет вызываться. А в юнит-тесте его не будет.
                                                                                      Не обязательно. При правильном построении исходных данных корректность сортировки можно проверить без «эталонных» выходных.

                                                                                      Ну вы же понимаете, что массив данных сам себя на сортировку не проверит. Что-то должно быть, или опорные данные для сравнения, или хотя бы проход по массиву для проверки его на отсортированность.
                                                                                      Во-первых, тесты должны быть значительно проще проверяемого алгоритма — тогда и ошибок в них будет заведомо меньше. Во-вторых, первый же запуск тестов проверит заодно и их.

                                                                                      И тем не менее, вы не можете обеспечить то, что тесты у вас будут значительно проще проверяемого алгоритма. Это вообще не от вас зависит, а объективно от требований к алгоритму. И вы должны будете проверять и алгоритм и тесты.
                                                                                      А кому нужна функция сортировки, работающая «в целом»?

                                                                                      Например, тем, кто платит деньги за разработку и ждет от вас результат. Вы можете гарантировать работоспособность с вероятностью 99% за N денег и M времени, и с вероятностью 99.9% за N*2 денег и M*2 времени. Если ваш софт управляет не штуками вроде обсуждаемых по соседству Боингов-737, а относится к подавляющему большинству других приложений, частный случай отказа которых никого не убьет, и даже не нанесёт сколь-нибудь существенных материальных убытков, то первый вариант куда более логичен.
                                                                                        +1
                                                                                        Ну вы же понимаете, что массив данных сам себя на сортировку не проверит. Что-то должно быть, или опорные данные для сравнения, или хотя бы проход по массиву для проверки его на отсортированность.

                                                                                        А в чем проблема прохода по массиву для проверки упорядоченности? Этому проходу всё ещё не нужен "эталонный" результат.


                                                                                        И тем не менее, вы не можете обеспечить то, что тесты у вас будут значительно проще проверяемого алгоритма.

                                                                                        Могу. Если тесты оказываются сложнее чем алгоритм — я обычно просто не пишу их. Но в случае с сортировкой это не так.


                                                                                        Вы можете гарантировать работоспособность с вероятностью 99%...

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


                                                                                        И все из-за того что я пожалел получаса на написание теста.

                                                                                          +1
                                                                                          И все из-за того что я пожалел получаса на написание теста.

                                                                                          А вы не напишете и не отладите все те тесты за полчаса. Вы реально потратите на них больше времени, чем потратили на ту сортировку. А ведь мы говорим только об одной функции, а сколько их ещё там будет. Полноценное покрытие тестами в среднем удваивает сроки разработки для большинства проектов. Выгода появляется лишь там, где очень сложное поведение и много внутренних взаимосвязей, либо что-то с высокой стоимостью отказа. Тесты однозначно нужны, если вы пишете трансляторы, движки СУБД и тому подобный софт, или какие-то платные сервисы, работающие в режиме 24х7. Но если вы относитесь к 99% остальных программистов, которые пишут бизнес-приложения, сайты/интернет-магазины, мелкие аппликухи для смартфонов и т.д., то расходы на юнит-тесты у вас не окупятся, даже со всеми теми возможными «коллеги неделю баг искали». Поэтому тут здравый подход, как по мне, следующий: заказчик платит — делаете. Вам-то только лучше, вам за это заплатили. Заказчик не платит, или пишете для себя и за свой счет, пишите без юнит-тестов и не переживайте, вы ничего ровным счетом не потеряете.
                                                                                            +1
                                                                                            А вы не напишете и не отладите все те тесты за полчаса.

                                                                                            Если ошибок не будет — напишу и отлажу. А если будут — значит, тесты были написаны точно не зря.


                                                                                            А ведь мы говорим только об одной функции, а сколько их ещё там будет.

                                                                                            И правда, сколько подобных (т.е. алгоритмических) задач у меня будет в типичном проекте? Моя личная статистика — в среднем 0,1 на проект...


                                                                                            Выгода появляется лишь там, где очень сложное поведение

                                                                                            А у сортировки, что, простой алгоритм?


                                                                                            Напомню, свою сортировку нужно писать только в том случае, когда находящаяся в стандартной библиотеке почему-то не подходит. Это автоматически отметает все простые алгоритмы.

                                                                                            0
                                                                                            А в чем проблема прохода по массиву для проверки упорядоченности? Этому проходу всё ещё не нужен "эталонный" результат.

                                                                                            Вам потребуется убедиться, что алгоритм не выкидывает дубликаты и не добавляет их, что он пользуется компаратором, в него переданным, а не случайно подвернувшимся под руку operator<, и так далее. Где-то в процессе вам придётся в тестах научиться подсчитывать и группировать элементы согласно предикату, что уже начинает плохо пахнуть.

                                                                                              0
                                                                                              Вам потребуется убедиться, что алгоритм не выкидывает дубликаты и не добавляет их

                                                                                              Ещё один проход по массиву. Ну прямо очень сложно...


                                                                                              что он пользуется компаратором, в него переданным, а не случайно подвернувшимся под руку operator<

                                                                                              Ну, на том же C# это попросту невозможно, если алгоритм обобщенный. На плюсах можно структуру-обертку сделать без оператора <. Или можно использовать компаратор, который не совпадает с естественным порядком, например x * 3 % n < y * 3 % n


                                                                                              Где-то в процессе вам придётся в тестах научиться подсчитывать и группировать элементы согласно предикату, что уже начинает плохо пахнуть

                                                                                              В языке с мутабельными массивами нет необходимости делать так сложно.

                                                                              0

                                                                              Это хорошо, прямо жаль стало, что вы не sim3x.


                                                                              Только вот как теперь доказать, что этого набора тестов достаточно?


                                                                              Это уж я заметаю под ковёр вопрос об определении достаточности, он тоже интересный.


                                                                              Ну и да, я не зря timsort взял. В алгоритмах я не оч шарю, но ЕМНИП конкретно timsort переключается на принципиально другой алгоритм сортировки для малых массивов. Не зная, что внутри функции, вы не сможете определить, что надо потестировать массивы разного размера, и где проходит эта самая граница разности, и не возникнет ли там спецэффектов где-то.


                                                                              Проще доказательство написать, ей-богу.

                                                                                0
                                                                                Только вот как теперь доказать, что этого набора тестов достаточно?

                                                                                Тесты — не про доказательства. Тесты проверяют, что не наделано глупых ошибок.


                                                                                Не зная, что внутри функции, вы не сможете определить, что надо потестировать массивы разного размера, и где проходит эта самая граница разности

                                                                                Тут согласен, надо достать эту константу из алгоритма и учесть ее в тестах.


                                                                                Проще доказательство написать, ей-богу.

                                                                                Не уверен. На языках с формальной верификацией придется долго объяснять компилятору тривиальные вещи (вы и сами на это жаловались), а на остальных ваше "доказательство" тоже может содержать ошибки.

                                                                                  0
                                                                                  Тесты — не про доказательства. Тесты проверяют, что не наделано глупых ошибок.

                                                                                  Ну тут смотря как к ним относиться. Если так — то да, конечно, согласен. Если же ожидать от тестов каких-то гарантий (что утверждалось кем-то сильно выше по треду неделю назаду) — ну, сами понимаете.

                                                                                  0
                                                                                  Проще доказательство написать, ей-богу.
                                                                                  если проще, то напишите

                                                          0

                                                          Делать рефакторинг с другими типами тестов.

                                                            0
                                                            Как делать рефакторинг с помощью функциональных тестов, если наружу у данного блока торчит одна ручка?
                                                              0

                                                              например?

                                                                0
                                                                Что например?
                                                                  0

                                                                  Пример блока с одной ручкой.

                                                                0
                                                                Если наружу у модуля торчит только одна ручка — то тем проще. Нужно описать подробно, что она делает и все юниттесты выкинуть за ненадобностью.

                                                                И да — я это вполне проделывал.
                                                                  0
                                                                  Отлично

                                                                  Так как проверить корректность его работы?
                                                                    0
                                                                    Сравнением результатов работы со спекой на эту самую единственную ручку, очевидно.
                                                                      0
                                                                      Как?
                                                                      Конкретнее пожалуйста
                                                                  0
                                                                  Я, честно говоря, не понимаю, в чём тут проблема. Если наружу торчит одна ручка, это вообще идеальная ситуация. Вы сразу увидите, что блок работает не так. А чтобы локализовать проблему, возникшую при рефакторинге, просто возьмите вместо юнит-тестов глаза и мозг. Вы же не делали рефакторинг с закрытыми глазами, случайно тыкая по клавишам? Значит, вы прекрасно знаете, что изменилось, и что могло поломаться. Ситуация, когда есть какой-то код, настолько сложный, что при рефакторинге вы понятия не имеете без тестов, что отвалилось, мне сдаётся совершенно надуманной. Хотя бы по той причине, что если вы не в курсе, как этот код работает, у вас есть ровно 0 аргументов за его рефакторинг и 100% против.
                                                                    –1
                                                                    Вы же не делали рефакторинг с закрытыми глазами, случайно тыкая по клавишам?
                                                                    В том-то и дело, что очень часто люди именно так и делают рефакторинг. Просто рандомно переносят строчки из одного места в другое, запускают набор тестов и «убеждаются, что всё работает».

                                                                    Конечный результат — куча странных багов, которые никто не может поправить, так как откуда они возникают — непонятно, а как программа в принципе работает — не знает никто. То есть вообще никто.

                                                                    Ситуация, когда есть какой-то код, настолько сложный, что при рефакторинге вы понятия не имеете без тестов, что отвалилось, мне сдаётся совершенно надуманной.
                                                                    Вы слишком хорошего мнения о «комбинаторных программистах». Я наблюдал ситуацию, когда проект был нашинкован в лапшу с помощьё Guice и ответ на вопрос «откуда тут вообще берётся e-mail заказчика» не знал никто. Соотвественно попытка исправить баг, при котором почта улетала «не туда» — превращалась в тот ещё квест. При этом юниттестов было полно, всё было отлично покрыто ими. Для того Guice и прикручивался.

                                                                    Хотя бы по той причине, что если вы не в курсе, как этот код работает, у вас есть ровно 0 аргументов за его рефакторинг и 100% против.
                                                                    Наоборот — это было преподнесено как достижение: нам не нужно знать что и как тут работает, потому что у нас покрытие тестами больше 95%. Тот факт, что при этом, почему-то, никто не может баг исправить, за разумное време, никого совершенно не смущало.
                                                                      0
                                                                      Хотя бы по той причине, что если вы не в курсе, как этот код работает, у вас есть ровно 0 аргументов за его рефакторинг и 100% против.

                                                                      Как раз наоборот. :) Если не можешь понять что делает код — его нужно рефакторить. А если понятно, то особо и рефакторить незачем.

                                                                        0
                                                                        Я тут бы поспорил. Мы же все как художники, вечно недовольны работой своих предшественников, и только ищем повод, чтобы её переписать :)
                                                                        А это такая себе ловушка, которая обычно создает куда больше проблем, чем решает.
                                                                          0

                                                                          Это да. Но я больше о производственной необходимости, а не о повышении самооценки :)

                                                                        +1
                                                                        Ситуация, когда есть какой-то код, настолько сложный, что при рефакторинге вы понятия не имеете без тестов, что отвалилось, мне сдаётся совершенно надуманной.

                                                                        Бывает объективно сложная бизнес-логика, в которой лень и некогда разбираться. Бывает 100500 краевых случаев, любой из которых может легко сломаться.

                                                                          0
                                                                          Бывает. Но тут как раз второй вопрос — зачем её вообще рефакторить, если вам банально с её работой некогда разбираться?
                                                                            +2

                                                                            Чтобы рефакторить некоторую часть не обязательно разбираться в ней всей.

                                                                +1
                                                                От функциональных отказываться нельзя никогда

                                                                Они всегда покрывают весь проект, тк пишутся по юзкейсам
                                                                Проблема с функциональными — они могут просто отдать вам некорректный результат. Но вот сузить область, где именно ошибка лежит они чаще всего не смогут
                                                            +3

                                                            Юнит тесты позволяют быстрее изменять существующий код, этот эффект в расчетах не учтен.
                                                            Кроме того, тестируемый код приходится писать менее связанным с другими частями проекта, что косвенно улучшает архитектуру.


                                                            Уменьшение вероятности ошибки далеко не единственный результат юнит-тестирования, строить выводы о миллиардных убытках на выбранной модели весело, конечно, но не совсем корректно.

                                                              +2
                                                              Буду признателен, если поделитесь статистикой, подтверждающей это.

                                                              Действительно, есть мнение, что юнит тесты упрощают поддержку, но также есть противоположное мнение, что юнит тесты сами требуют поддержки и периодического переписывания или даже затрудняют внесение изменений в проект т.к. разработчики ленятся переписывать старые части системы, покрытые тестами. Я не знаком с исследованиями на эту тему, которые бы подтверждали бы одно или другое суждение, поэтому в модель включены только прямые затраты, которые можно хоть как-то оценить.
                                                                +1

                                                                Но ведь именно это главный аргумент сторонников TDD, которые были упомянуты в самом начале.
                                                                Если кратко резюмировать их логику — написание тестов отдельно/после кода требует сильно больше времени и сил через правильное написание кожа и тестов в параллель.
                                                                Те результаты статьи совершенно правильные — написание тестов классическим образом — пустая трата времени и денег.
                                                                Быть может их просто как-то по другому писать надо?

                                                              +17
                                                              Основные плюсы юнит-тестов вы так и не озвучили.
                                                              Дело в том что в отличии от других уровней контроля качества, юнит-тесты лежат максимально близко к коду. Если человек пишет юнит-тесты, то это непременно влияет на процесс разработки.
                                                              Вы говорите насколько написание юнит-тестов увеличивает затраты на задачи,
                                                              но не говорите насколько они уменьшаются при наличии ранее написанных тестов.
                                                              Можно вспомнить хотя бы про то, что наличие юнит-тестов позволяет не бояться вносить изменения в код, а так же про то что юнит-тесты заставляет программиста писать код лучше (кривой код очень сложно покрывать юнит-тестами).
                                                              А еще юнит-тесты можно применять в качестве документации к модулям системы, у новых участников команды не будет возникать вопросов как использовать тот или иной компонент.
                                                              А еще… Да много еще чего.
                                                              Короче говоря, юнит-тесты это больше про процесс разработки, а не про контроль качества.
                                                                +3
                                                                Оглядываясь назад в те времена когда я не писал тесты, мне иногда кажется что я был очень смелым.
                                                                Но на самом деле тут должна быть картинка «Слабоумие и отвага» с Чипом и Дейлом.
                                                                Сегодня выполняя задачу без тестов я чувствую себя лгуном, так как говорю что задача сделана, а гарантий этого я не даю даже самому себе.
                                                                  +4

                                                                  Да вы и сейчас не даёте никаких гарантий, ибо юнит-тесты не гарантируют, что всё в сборе будет работать.

                                                                    +2
                                                                    Гарантия это про несение ответственности, а не про полное отсутствие багов.
                                                                      0
                                                                      Я конечно догадался что вы про интеграционные тесты. Просто на низком уровне интеграционное и модульное тестирование не так уж и сильно отличается. Если я мокаю в тесте одну из зависимостей с сохранением интерфейса и проверкой передаваемых в нее параметров, это все еще модульный тест или уже интеграционный?
                                                                        +1

                                                                        Нет, не про интеграционные.
                                                                        https://habr.com/ru/post/351430/

                                                                          +1
                                                                          Я читал вашу статью и хотя в целом я поддерживаю разделение элементов системы на модули и компоненты, концептуально это не играет роли, сотрите из своей схемы модули и компоненты превратятся в те же модули. К тому же, ваша схема неверная, у вас связи от модуля идут только к одному компоненту, в реальной жизни это не так. К примеру вызов библиотечной функции можно считать обращением к модулю и вряд ли вы захотите использовать библиотеку не покрытую тестами.
                                                                          Я это к тому, что если считать компоненты независимыми черными ящиками со строгим контрактом, то ваши компонентные тесты вполне можно считать модульными, а если знать о наличии модулей, то эти тесты по определению интеграционные/функциональные.
                                                                            –1

                                                                            Вот вы говорите прочитали статью, но продолжаете называть компоненты модулями и на этом основании доказывать полезность модульных тестов. Модуль — единица кода (обычно — один файл или его часть). Компонент — единица функциональности (например, класс со всеми своими транзитивными зависимостями).

                                                                              +1
                                                                              И продолжаю называть. Потому как неспроста никто однозначно не говорит что же такое модуль в модульном тестировании. Вы правы что модуль это часть приложения которую можно протестировать независимо, но нужно еще определить независимо от чего именно его нужно тестировать.
                                                                              К примеру, нужно ли тестировать систему независимо от работы стандартной библиотеки языка.
                                                                              Просто вы для себя в качестве модулей выбрали так называемые «компоненты» (и я тоже). Что при этом мокать это всегда интересный вопрос, как правило при принятии решения учитываются время прогона теста, сложность подготовки окружения для зависимости, наличие модульных тестов для зависимости.
                                                                                –2

                                                                                У модуля вполне однозначное определение — это кусок исходника, а не часть приложения. И стандартную библиотеку тоже над мокать.Тем более, если эти стандартных библиотек больше чем одна. Многие понимают бессмысленность этого действа, поэтому для стандартной библиотеки делают исключение. Некоторые делают такое исключение и для сторонних библиотек. И лишь немногие идут до конца и понимают, что их библиотеки от не их библиотек ничем принципиально не отличаются и перестают мокать всё подряд. Однако уже с первой незамоканной зависимостью тест по факту перестаёт быть модульным. Но по инерции люди продолжают называть модульными тесты, где не замокана ни одна зависимость.

                                                                                  +1
                                                                                  Окей, we need to go deeper.
                                                                                  Не нужно ли при написании «истинного» модульного теста еще мокнуть реализацию языка программирования, ведь от нее тоже много чего зависит? И если мы этого не делаем, то наш модульный тест по факту опять интеграционный.
                                                                                  И даже если мы выполним эту задачу (хоть это и невозможно), можно пойти еще глубже и начать думать что для «истинного» модульного теста нам еще нужно мокнуть «железо» на котором этот тест запущен.
                                                                                    +1

                                                                                    Мы это с ним уже обсуждали. Модульных тестов не бывает. А так как их не бывает то и модулей не бывает (см его статью — модуль это то что можно протестировать отдельно). Ссылки на Фаулера с sociable unit tests не катят.

                                                                                      0

                                                                                      А давайте не утрировать?
                                                                                      Называть компонентные тесты интеграционными тоже не корректно.

                                                                                        0

                                                                                        У вас свое собственное определение компонентов. Обычно это — единица развертывания

                                                                    +2
                                                                    Ну и последнее, написание юнит-тестов не всегда замедляет решение задачи, если задача это не какая-то элементарная вещь, то новые юнит-тесты начинают работать еще до завершения текущей задачи и тем самым ускоряет ее решение, хотя тут вы вероятно мне не поверите.
                                                                      +2
                                                                      Это все отлично. Только вот можете ли вы описать эту выгоду в денежных еденицах? Не «значительно лучше» и т.п. что есть качественная оценка… А количественная, например — внедрение юнит тестов позволит нам решить эту задачу, нанаяв всего 7 человек, вместо 15 при условии неизменного срока разработки? Или внедрив юнит тесты мы уменьшим время простоя нашего сайта с 48 часов в год до 8 и это принесет нам ххх дополнительной прибыли, которая будет больше, чем затраты на внедрение и поддержку юнит тестов…
                                                                        0
                                                                        Данную оценку вполне можно дать при достаточности исходных данных… Но их как правило достаточно не бывает.
                                                                        Хотя вру, бывает — когда заранее известно что разрабатываемая система временная и будет выкинута или не будет никак дорабатываться. К примеру, системы-костыли необходимые только на переходный период изменения какого-то бизнес-процесса, мосты между legacy-системами, всякие хакатоны и т.п.
                                                                        Прошу заметить, что я не утверждаю что в этих системах не нужны тесты, я говорю о том, что «стоимость» написания или ненаписания тестов тут можно как-то оценить.
                                                                        Если же система предполагает развитие, во что она потом превратится и нужны ли ей будут тесты большой вопрос, может быть бюджет на систему закончится еще до ее внедрения…