Pull to refresh

Comments 93

Вот все было хорошо, пока top-down подход антипаттерном не назвали :)) ИМХО одно другому не мешает, нужно совмещать

Спасибо. Справедливо.
Перефразировал, подчеркнув акцент на контр-продуктивности персонально для себя.
UFO just landed and posted this here

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

TDD это как степень по гендерным наукам. Все о ней говорят, но никто не видел, чтоб это работало на практике или приносило деньги. Приходится продавать семинары по TDD.
Все эти подходы просто придумываются людьми совершенно далекими от разработки. Они просто не понимают, что в реальной жизни никто не даст переписывать ни код ни тесты по десять раз. Бизнес заказчику невозможно объяснить, зачем нужен рефакторинг и почему важно обновлять кодовую базу на новые версии фреймворков. Люди не пишут тесты не потому, что им лень, а потому, что на таску требующую пары дней работы выделяют пару часов. А потом приходят университетские работники и начинают разглагольствовать, что хорошо бы писать код правильно. Да, неплохо бы. Только, к сожалению, за это денег не платят.

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

Не совсем так, по моему опыту, приходит специалист, умный, красивый, энергичный. Начинает творить, так что клавиатура дымится, тестов, естественно никаких не надо, мы сразу всё правильно пишем.
Спустя год-другой конечно уходит, не вынеся всей тяжести свалившегося бытия (не всё пишется правильно с первого раза, а потом ещё и ломаться начинает то, что работало, хотя казалось бы), в другую контору, на большие деньги (опыт же вырос у специалиста, какие-то навыки прокачал, пару строчек добавилось в CV).
А время на фикс багов тратят уже другие.
Без тестов, обновления версий фреймворка и рефакторинга можно двигаться быстрее, но только первое время. А дальше вы поневоле начнете платить по техническим долгам. И более-менее адекватный бизнес это понимает.
В Гугле, в Андроиде есть баги не пофикшеные с 2016 и до сих пор актуальные. Я на своих 15 годах опыта тоже таких адекватных не встречал. Так что где таких взять — я не знаю.

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

В Гугле, в Андроиде есть баги не пофикшеные с 2016 и до сих пор актуальные.

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

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

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

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

Проблема в том, что объяснять нужно на языке бизнеса, а у инженеров с этим и в принципе плохо, и конкретики не хватает типа "внедрение TDD потребует 2 командо-месяца, увеличит скорость дальнейшей разработки на 15%, но сократит время на фикс багов на 80% и время разработки новых фич на 50%" просто потому что это будет враньё фантазии необоснованная оценка. Инженеры, в большинстве своём, не умеют думать и говорить в режиме "главное клиента завлечь, а потом и бюджеты будут, и сроки — коней на переправе ведь не меняют"

Древняя статья про эффективность TDD в цифрах — www.infoq.com/news/2009/03/TDD-Improves-Quality
Вполне удобная аргументация от инженера бизнесу.
А в контексте топика я бы добавил к цифрам, что это еще и экономит мыслительную энергию разработчиков (не всех).
Статья взята с потолка. Если сравнивать, то нужно сажать 2 команды одинаковой квалификации, одной работать по TDD, второй писать код, а потом тесты.

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

Но так никто не делал.
Вся суть тут
Defect density of comparable team in organization
but not using TDD
W X Y Z
Defect density of team using TDD 0.61W 0.38X 0.24Y 0.09Z

Что за сравнимая команда, которая без TDD генерила в 11 раз больше багов? Команда обезьян? Статья полная чушь, данные взяты с потолка.

Это такая стадия отрицания?

И мне кажется, что инженеры в том числе должны это ему объяснять.


Инженеры и объясняют. Но инженеры очень мало понимают в бизнесе. И поверьте, наемному CTO пофигу, какой там будет техдолг через 3 года. Ему главное дать на гора новый функционал (иконки, менюшки и прочую лабудень) и получить бонусы. А дальше он уйдет в другую компанию.

Гугл как раз тем и знаменит, что говорят они много, а делают мало. Я как-то по долгу службы админил гмейловскую админку в организации на 60 человек. Каждый клик в «отзывчивом» интерфейсе занимал 30-40 секунд. Список груп — жди. Пользователи в группе — жди. Список приложений — жди. Все мои знакомые маркетологи говорят, что adwords это адище, статистика вообще не бьется с логами серверов.

Вы мне можете что-то затирать про TDD, но я вижу результат и он ГОВНО. Мне пофигу сколько там тестов и сколько этапов собеседования, я вижу что компания пишет плохой софт и продает его за деньги (5 баксов с пользователя в месяц за неработающую админку и отсутствие хоть какой-либо автоматизации (гугл скрипты это отдельная песня))
он ГОВНО

Это вы просто вы предпочитаете смотреть на говно.


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

Начнем с того, что весь формат HTML+CSS+JS для построения UI отвратителен и убог. Вся сложность написания браузера связана именно с этим.

Второе. Любая организация, которая сможет понять, что она сможет зарабатывать миллиарды на продукте сможет его написать. Хром для гугла это такая же дойная корова сбора метрик, как и собственно поиск. Еще бы они не обновляли свою курочку с золотыми яйцами.

Ну и в третьих. Сколько нормальных продуктов разработала IT корпорация в 60к человек? Сколько из них разработала в последние 5 лет?
Нет, почему, это абсолютно релевантные для бизнеса вещи. С накоплением технического долга начинает падать скорость деливера фич, равно, как их качество, начинаются сложности с поиском новых сотрудников на легаси. Это всё ощутимо для бизнеса, и бизнес не сможет закрывать на это глаза. Другое дело, что на первых этапах стартапа порой действительно лучше делать по принципу «тяп-ляп и в продакшен», просто чтобы оценить потенциал идеи. Но потом рефакторить всё это бывает уже очень больно.

Кто должен объяснять бизнесу насколько падает скорость и т. п. в цифрах?

Если нужны более-менее правдивые цифры, то без аудита (возможно стороннего) наверное никак. Однако такие вещи как «80% работы программиста тратится на разгребание существующих багов», «тестирование новых фич становится адом из-за большой связности системы», «фича такой-то сложности сейчас релизится столько, а год назад писалась в два раза быстрее» видны и без тщательных оценок. Плюс частенько бывает, что собственники или сами выходцы из IT, или они хотя бы адекватные, чтобы данные проблемы понимать.

Бывает что по итогам принимается решение не рефакторить и наращивать техдолг дальше, но это тоже вполне рабочая стратегия.
очень даже рабочая стратегия, нашлепать побыстрому на рубях/php чтобы оценить приживется ли продукт на рынке.
если будет большой спрос, все равно придется переписывать медленные места полностью или вообще менять архитектуру.
никому не интересны юнит-тесты если при стократном росте все равно все идте на помойку и переписывается на яве/плюсах
Гораздо чаще бывает вариант «ни туда ни сюда». Нашлёпанный по-быстрому проект не выстреливает и стократного раста не случается, однако оказывается вполне рабочим, кормит команду и приносит небольшую прибыль инвесторам. И в таком случае не очень понятно что делать с кодом, потихоньку превращающимся в комок грязи: ресурсы на рефакторинг ограничены, очередь фич огромна. Нанять людей чтобы переписать проект нет финансовых ресурсов, бросить существующую команду на рефакторинг — нет временны́х ресурсов: пока мы будем рефакторить, конкуренты будут деливерить фичи и переманивать наших клиентов. С другой стороны, если бы проект с самого начала писался по правильным практикам — скорее всего бы он в плюс так бы и не вышел. И какие пути решения здесь — не очень понятно.
Вот прямо золотые слова.
западные компании уже все разузнали за нас — идут к венчурным капиталистам и рейзят миллионы долларов.
рисуют красивые презентации с розовыми единорогами и как они захватят весь мир, дайте только деняк чуть.

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

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

В гугле пишут тесты. Есть даже такая книжка "How Google Tests Software". То, что есть какие-то баги не значит, что тестов не пишут.

Ну почему же вы сразу лень отсекаете? Это вполне естественное чувство/поведение человека. Особенно, когда 8 часов в день делаешь одну и ту же работу (не делая полноценные перерывы с переключением контекста). Особенно, когда человек находится наедине с самим собой — когда работает один, а не в паре или на встречах, и когда за спиной только стена.

Только, к сожалению, за это денег не платят.

У меня, получается, другой опыт. Мне платили намного более низкую ЗП, когда я клепал задачи за задачей в местах, где тестирование делал сам программист. И развивался в профессии я очень медленно.
А, пока что, самая большая моя ЗП была в тех местах, где стараются писать юниты, где есть отдел автоматического тестирования (E2E-тесты) и где менеджеры и скрам-мастера просят пробовать XP практики и все-таки писать юниты тех, кто их избегает. И развиваться в таких местах я стал намного быстрее.
Все эти подходы просто придумываются людьми совершенно далекими от разработки.

Вопрос: от какой именно разработки?

Есть разработка, которая про «настрочить скриптец на коленке», когда полученный программный продукт не то что релизить, а запускать второй раз никто не собирается; а есть, которая «кровавый энтерпрайз», когда софтина живет и развивается десятки лет, а в разработке участвуют много-много независимых команд. И есть всё, что где-то между ними: там тебе и веб-студии, и аутсорсеры-фрилансеры, и разной мелкости корпоративные иэрпишечки-сиэрэмочки, и все другое прочее.

Если проект находится на шкале сложности где-то ближе к первому варианту, то не очень и удивительно, что тестов нет и на рефакторинг время не выделяют. Если где-то ближе ко второму, тогда уже без более-менее системного подхода проект начинает быстренько деградироват. И тут либо за ум взялись, либо загнулись совсем.
UFO just landed and posted this here
никто не видел, чтоб это работало


Я видел. Качество кода повысилось, багов стало меньше, поддерживать и дорабатывать продукт легче. А каких еще результатов вы ожидаете от tdd?

А как со скоростью работы? И было ли нормальное покрытие тестами до внедрения TDD?

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

Что же касается замедления относительно работы вообще без тестов… Ну, если у вас настолько компетентные разработчики, что они могут писать сразу без багов и с хорошей архитектурой, то тут, наверное, проигрыш раза в полтора-два. Но по факту, как мне кажется, проигрыш процентов в 20 с одновременным повышением качества и надежности кода.
UFO just landed and posted this here
А что значит «некуда было приспособить»? В моем случае успех достигался простым применением методологии при решении новых задач. Безусловно, могут возникнуть проблемы при внедрении модульного тестирования в неприспособленный для этого код. Но у нас, как я говорил, модульные тесты уже по факту были, хоть и не выполняли 80% своих функций.
UFO just landed and posted this here
Это же не юнит-тесты? Ведь там не тестируются отдельные функции. А как и зачем тестировать отдельные функции в этом случае — непонятно.

Прошу прощения, что влезаю. Если я что-то правильно понял, то в Вашем случае выбор языка исключает львиную часть кейсов, когда тестирование чистой функции будет реально полезным. Осторожно предположу, что юнит-тест для такой функции может пригодиться, если система типов по какой-то причине не позволяет достаточно точно проконтролировать входные параметры и возвращаемые значения. Например, когда нужно достаточно большое (чтобы нельзя было просто перечислить в другой тип) подмножество значений некоторого типа, и при этом сложность проверок на корректность внутри функции оправдывает создание внешних тестов. Могут ли в Haskell быть такие кейсы, хотя бы чисто теоретически? Насколько я вообще что-то понимаю про юнит-тестирование, его польза обратно пропорциональна гибкости и одновременно строгости системы типов конкретного языка. Смысл тестировать то, что и так будет проверено компилятором?

UFO just landed and posted this here
Ну, получается, юнит-тесты на самом деле по большей части отменяются выразительной системой типов.

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

UFO just landed and posted this here

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


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


По TDD вы пишите что-то вроде assert(5, sum(2,3)), пишите sum = (a,b) => a+ b и всё (если в требованиях нет чего-то типа обработки переполнения), а всякие assert(-2^63-1, sum(2^63, 1)) пишите уже для покрытия, для закрепления, для документации

Как я понимаю, тестировать нужно логику. Если ваша функция получает a, преобразует его в b и возвращает / вызывает другие функции с этим значением, то именно эта логика преобразования и должна тестироваться. Т.е. передавать исходные данные и проверять, что вы получаете ожидаемый результат. Вы уже делаете подобные вещи, но на интеграционном уровне. А тдд плюс ко всему еще и переворачивает этот порядок. Т.е. перед тем, как написать новую функцию/модуль, вам нужно сначала сформулировать, что он собственно должен делать.
UFO just landed and posted this here

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

Интеграционные тесты работают на уровне модулей, юниты — на уровне функций. В принципе, что вам мешает писать тесты не только для модулей, но и для отдельных функций в них?
По тдд, в принципе, можно работать как на уровне целого приложения, так и на уровне модулей и отдельных функций. Писаться будут соответственно функциональные, интеграционные и юнит тесты. А дальше каждый сам решает, какой уровень детализации тестирования ему нужен. Я б предложил по возможности попробовать все 3 и сравнить результат.
UFO just landed and posted this here
Тогда, чтоб протестировать всю логику этих модулей вам в тесте на главную функцию надо перебрать все возможные варианты входящих параметров для остальных пары десятков функций. Если вам это несложно, то можно ограничиться и одним тестом. Но обычно удобно тестировать апи, которое под капотом использует не больше нескольких функций.
Тогда, чтоб протестировать всю логику этих модулей вам в тесте на главную функцию надо перебрать все возможные варианты входящих параметров для остальных пары десятков функций.

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


Тесты только на то, что есть, вернее будет в коде, который пишем на данной итерации. Тесты на покрытие областей определения функций пишутся вне рамок TDD

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


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

Но, по идее, юнит-тесты — это ж на уровне отдельных функций, разве нет?

Формально юнит-тесты — это модульные тесты, синоним. Вы покрываете изолированный модуль тестами и тесты называются модульными или юнит-тестами. Будет каждый отдельный тест-кейс проверять одну функцию или 100500 — не суть, он всё равно будет юнит-тестом, если модуль тестируется в изоляции от других модулей.

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

TDD отлично работает для задачи, для которой уже заранее примерно ясно, как будет выглядеть решение. Желательно чтобы еще и зависимостей у решения не было. Хороший пример — всякие алгоритмические задачки типа leetcode и того, что на интервью спросят.


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


Или вот про зависимости. Начали по ТДД, раз тест, два тест, три. А потом добавили зависимость — и все предыдущие тесты надо поправить. Потом снова. А затем переосмыслил подход и избавился от зависимости — снова меняем ранее написанные тесты.


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

Пишешь-пишешь, оппа, оказывается есть такой подводный камень и пол решения надо переделывать, включая интерфейс взаимодействия с внешним миром

Так это и есть принцип TDD: вместо того, чтобы сначала всё продумать, создать API, написать тесты (test-first), затем под них код, TDD заставляет выкинуть всё это из головы и двигаться маленькими шажками. Естественно, в этом случае API будет по многу раз переписываться.

Так зависимостями надо управлять. И TDD, кстати, помогает бороться с соблазном плодить зависимости.

отдельный тип задач, где ТДД (а точнее, написания теста до реального кода) помогает

Автор статьи совершенно верно заметил, что TDD — это совсем не написание тестов до реального кода. То, что вы описываете, — это, спору нет, тоже полезно, но это не TDD.

Да, TDD хорош тем, что принуждает к написанию кода в соответствии с принципами написания хорошего кода: модульность, изолированность. Очень хорошо помогает новичкам в программировании.

Написание юнит тестов в принципе подталкивает к модульному и изолированному коду. Безотносительно к TDD

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

Ложный тезис. Особенно, если код пишет один человек, а тесты другой, да без права править код.

Эм. Ну… да, пожалуй.
Я такого сам не видел и звучит дико — но допускаю, что и так бывает.

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

А, ну это все же совсем другой сценарий. Я отвечал в контексте задачи, где можно рассуждать о применении или не применении TDD.
Конечно, ты не получишь модульный код, если перед тобой в принципе не стоит задача написания кода.

Да даже если стоит, но в варианте: напиши фичу, отдашь на тестирование и пиши тесты на неё — их можно без QA мержить, не надо будет перетестировать

TDD плох тем, что он работает только в элементарных случаях.

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

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

если кодишь продукт сам или в малой команде высококвалифицированныхз спецов — интеграционных тестов хватит.

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

К слову, в TDD три шага (помним мантру "красный-зеленый-рефакторинг") — все три важны. Если поленились отрефакторить, то это уже не вина ТДД

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

Или вот про зависимости. Начали по ТДД, раз тест, два тест, три. А потом добавили зависимость — и все предыдущие тесты надо поправить. Потом снова. А затем переосмыслил подход и избавился от зависимости — снова меняем ранее написанные тесты.


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

А, спасибо, я хотел раскрыть этот момент.


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


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

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

Безусловна вся программа это собрание зависимых между собой методов и функций. И тут важную роль играет тестовый инструментарий. Например тот же pytest позволяет создавать своеобразные каскады тестовых данных. Это когда ты создаёшь фикстуры используя в качестве родителей другие фикстуры. И вопросы зависимостей решаются хорошо. Вот класс А он ни от кого не зависит (ну или с некоторым отступлением позволяет себя так рассматривать), вот класс Б он зависит от класса А.
  1. Создаём тесты на класс А.
  2. Создаём фикстуру, которая возвращает класс А.
  3. Создаём тесты на класс Б в которых используем фикстуру с А.
  4. Создаём фикстуру, которая возвращает класс Б.
  5. Создаём тесты на класс А, для ситуации когда ему нужен Б. И применяем имеющиеся фикстуры.


Далее, берём задачу и думаем, что нам надо. Нам надо изменить класс А, каким образом? Добавить функционал или провести рефакторинг? Соответственно работаем. Добавляем в тесты и фикстуры изменения, чтоб получался ожидаемый результат.

Пускаем тест, не пашет. Берём лопату и идём копать пока не отработает ожидаемо.
Я когда-то на RSDN писал про TDD:

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

Test-driven development — это воплощение идеи «ленивых вычислений» в самом программисте.

Тебе облом. Тебе ОБЛОМ. Тебе облом в кубе. Но надо делать. Ты смотришь на обломки старых недоделок. Ты понимаешь, что нет никаких сил разбираться во всём этом, тебе абсолютно по барабану, что внутри делается каким кодом и почему. Но тебе НАДО это сделать.

Ты хочешь, чтобы оно хоть что-то сделало. Ты пишешь тест, чтобы оно хоть чихнуло. Ты его запускаешь… кодишь… запускаешь… кодишь… запускаешь… кодишь… запускаешь… кодишь… и всё это через большой, тотальный ОБЛОМ.

Наконец заработало, ты его спихиваешь и с чистой душой идёшь в отпуск.

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

И как ни странно, потом оно работает. До следующего изменения требований.:))

Да здравствует лень.


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

> его цель — проверить только следующий маленький шаг, которые разработчик собрался реализовать в ближайших строках кода в следующие 2–5–15 минут

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

> человек чаще всего неосознанно выбирает задачу понятную

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

Я не очень силен в теории алгоритмов. Буквально только книга «Грокаем алгоритмы». Но я вижу некую схожесть TDD подхода и подхода «жадных алгоритмов», когда на каждом шаге определяется оптимальное решение только для этого шага. У этого есть и минусы, но есть и плюсы.
В этой книге описывает алгоритм Дейкстры и говорится, что это пример «жадного алгоритма». Его, наверное, нельзя отнести к «великим» (книга то для начинающих), но меня впечатлила его изысканность. Когда понятными шагами была решена задача, которую я не понимал как решать в целом.
У этого есть и минусы, но есть и плюсы.

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

Если я правильно понял вашу первичную идею, то путем TDD невозможно написать великий алгоритм. Т.е. дойти до решения, которое еще не известно.
Постановка задачи «написать quicksort» подразумевает, что алгоритм уже известен. Т.е. любая реализация ни докажет, ни опровергнет ваш тезис, потому что постановка задачи уже содержит способ имплементации. TDD не заменяет знание, он меняет путь прохождения по неизвестному.

Тут вижу два варианта. Или оставлять требование quicksort и тогда я буду проверять состав деревьев на каждом шаге, что странно попахивает. Или поставить требование на сложность алгоритма n*log(n), и тогда не факт, что у меня получится quicksort.

С другой стороны, если бы речь шла о «написать сортировку», то я на текущем уровне своего развития, скорее всего, написал бы сортировку вставкой или сортировку выбором. Получил бы пачку тестов «входные данные» => «выходные данные», которые могли бы принести пользу для проверки любой другой сортировки.
то путем TDD невозможно написать великий алгоритм. Т.е. дойти до решения, которое еще не известно.

При точном следовании — да, просто невозможно. Но дело не в этом.


Постановка задачи «написать quicksort» подразумевает, что алгоритм уже известен.

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


С другой стороны, если бы речь шла о «написать сортировку», то я на текущем уровне своего развития, скорее всего, написал бы сортировку вставкой или сортировку выбором.

Дело не в уровне. Дело в ограничениях: вам просто не дадут сделать что-то другое.

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

Я встречал рекомендации писать code spike если работа исследовательская, а потом писать prod код смотря в этот spike по TDD. Т.е. вся эта дисциплина типа pair programming относится к prod коду.

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

Алгоритм не получится изобрести или программу, его реализующую написать?

Алгоритм не получится изобрести или программу, его реализующую написать?

Алгоритм. Но с программой тоже будут проблемы, если не резать совсем уж мелко и искусственно (например, в partition выносить внутренность цикла в отдельную функцию).

Ну TDD в целом не для изобретения алгоритмов. Особенно алгоритмов отличительніе особенности которых лежат в нефункциональной области (вычислительная сложность и всё вот это вот)

Для меня лично применение практик TDD, стало обыденностью. Но нужно понимать, что тот же господин Бек, решает в первую очередь вопросы собственного рабочего дискомфорта. Например его онанизм на зелёную полоску это просто медитативная техника. Многие для того же используют ручное форматирование кода или попивание кофейка — сделал глоточек (поставил пробельчик) — обдумал — ещё глоточек (пробельчик). И всё в том духе.

На мой субъективный взгляд, основная ошибка применения TDD это забывчивость о задаче проектирования решения, собственно поставленной задачи. Мы НЕ движемся маленькими шагами, мы следуем техпроцессу. А тест это и есть техпроцесс, причём пошаговый техпроцесс. В различных инженерных отраслях техпроцесс уже больше сотни лет норма. Отличие лишь в том, что пишутся огромные талмуды специальными людьми, а программист будет писать небольшой локальный техпроцесс, для себя. Никто из химиков, машиностроителей и прочих, не бежит заглядывать в ректификационную колонну или выдёргивать из автомобиля мост, чтоб посмотреть как оно ДОЛЖНО работать. Он читает техпроцесс. А токарь не смотрит как эта самая машина едет мимо, видит мост и делает также. Он берёт техпроцесс и делает деталь согласно ему.

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

Тест помогает осознать последовательность шагов, применяемые решения и узкие места. А также показать, что и с каким результатом сделано. Не нужно применять его как медитативный инструмент, или вам перекрыли доступ к кофе?

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


Они в самом начале пишутся и потом реюзаются и вот в этом узком месте ТДД и правда неплох.


А потом приходит практика. Вот, к примеру, в таких статьях умники никогда не пишут как покрывать слой View.

Общего ответа нет, потому что этот слой архитектурно множеством способов реализован, но если он хоть немного близок к view = render('viewname', viewdata), то вполне нормально тестируется разными подходами.

Обычно в таких тестах тестируется не логика отображения, а раз за разом корректность работы фреймворка.

Это зависит от того, что собственно ассертится в тестах. Если мы просто проверяем наличие "кусочков" viewdata в результатах рендеринга, то мы проверяем, что:
а) render в принципе рендерит 'viewname', в том числе что 'viewname' синтаксически корректно описан и ошибок в render нет
б) что мы не забыли включить вывод этих кусочков во 'viewname' и с данным набором viewdata оно выводится


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

В такой формулировке проблемы складывается ощущение, что приложения на ReactJS сложно тестировать, хотя это не так. Я в данный момент читаю книгу TDD на React — www.packtpub.com/product/mastering-react-test-driven-development/9781789133417. Те же самые принципы в действии, а самое сложное — моки и стабы, которые являются сложностью и при test-last.

В этой статье не затрагивались примеры самого использования TDD. Но, мне кажется, на ваш вопрос отвечает раздел Test-First Thinking.
Вы же как-то узнаете, что на экране правильно отобразилось то, что ожидается (чаще всего просто текст на экране)? Как вы демонстрируете бизнесу, что сделали функцию?
Практически все, что может проверить человек, может проверить и тест. Осталось только понять, из чего у программиста складывается уверенность в правильной работе кода.
Практически все, что может проверить человек, может проверить и тест.

Тут есть нюансы. Есть разные виды тестов и не все могут проверить то, что может проверить человек. Тот же текст на экране сложновато, особенно если сначала надо написать тест — изображение текста сформировать руками и искать его в экранном буфере с учётом сглаживаний и т. п.? Можно делать некоторые допущения типа "если в DOM-дереве появилась такая-то строка, то считаем её появившейся на экране", но гарантий такие тесты обычно не дают.

" — Я тут игру разрабатываю, opengl, шейдеры, все дела. Проблемы с перфомансом, что посоветуете?"
" — Микросервисы же! Шардинг, машин лернинг, блокчейн, бигдата! TDD!"


Программисты, несмотря на всю свою образованность, любят натягивать сову на глобус.
Есть разные виды софта — веб-сайты, симуляторы, интерпретаторы, компиляторы, игры, CAD. Есть разные стадии разработки — продукт с нуля, фича с нуля, фича в жутком Легаси итд итп. Есть разные процессы — продуктовая разработка, аутсорсинг итд. Как "готовить" — тоже важно, индусы на аутсорсинге могут нафигачить тестов в стиле return true; (был опыт). И TDD здесь — замечательный молоток для забивания гвоздей, но если у вас лесопилка или вы резьбой дереву занимаетесь — конечно может и не пригодится. А если кажется что подойдёт, но люди попадают молотком себе по пальцам — проблема может быть и не в TDD.

UFO just landed and posted this here
UFO just landed and posted this here

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

Sign up to leave a comment.

Articles

Change theme settings