За счет чего TDD “драйвит” разработку

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

Поэтому я не хотел писать еще одну статью с описанием техники Red-Green-Refactor. Мне хотелось взглянуть на TDD немного глубже и описать, как и почему TDD влияет на поведение человека.

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

Мои первые шаги в TDD

Я работаю web-разработчиком 12 лет, и недавно я поменял свой базовый стек с php (CMS-ки) на javascript (React). Немного обидно и, для кого-то, удивительно, но познакомился с TDD я совсем недавно (в этом году), хотя многие из статей, которые я читаю, датируются далеким 2013 годом. Еще интереснее то, что познакомило меня с TDD не рабочее окружение или корпоративные стандарты, а Скрамгайд и самостоятельная подготовка к сертификации Professional Scrum Developer на scrum.org.

И вот в какой-то момент я оказался один на один с целью прочитать книгу “Test Driven Development: By Example” от Kent Beck. В тот момент у меня было некое понимание, что такое TDD, и оно преимущественно совпадало с коллегами, которые также что-то слышали о нем, но толком не пробовали. В двух словах, я думал, что “TDD — это те же самые юнит тесты, только написанные до имплементации”. Звучит немного отпугивающим и сложным, но мне понравилась идея. И я начал читать…

В районе 50-ой страницы ко мне пришло озарение, насколько ложным и неправильным было мое прежнее понимание. Тесты, написанные при TDD, — это другие тесты, категорически и совершенно другие тесты… по их логике, по их коду, по их смыслу. Если вкратце, то такой тест не должен соответствовать и проверять требование задачи, его цель — проверить только следующий маленький шаг, которые разработчик собрался реализовать в ближайших строках кода в следующие 2–5–15 минут. Пример, как это может выглядит — Example of TDD by H. Koehnemann, и обратите внимание, что acceptance test пишется уже в самом конце.

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

И вот они:

Верхнеуровневый список задач (todo list)

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

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

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

Внезапно появилась новая гениальная идея? Не переключайтесь на неё, отправьте её в список. Потом к ней вернётесь.

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

Test-First Thinking

Test-first мышление — это уже нечто большее чем техника — это сдвиг в видении задач и подхода к их решению. Обычно, перед началом имплементации, разработчик задается вопросом “как я реализую эту функцию?”. Основная идея test-first подхода в том, что такой вопрос смещает фокус с задачи на имплементацию этой задачи. Это смещение может привести к выстраиванию “воздушных замков”, излишней преждевременной оптимизации, нарушения принципа о простоте из Agile манифеста, не говоря о конкретных YAGNI и KISS правилах разработки. Но даже если этого не произойдет и код не будет нарушать эти принципы, это все равно не ответит на вопрос “как я узнаю, что я действительно достиг своей цели?”.

Измерение достижения цели — это то, что делает цель целью. Без измерения это уже не цель, это только желание, неформализованная хотелка. Бывало ли у вас, что вроде бы все шаги сделаны, а понимания, что желаемое получено, — нет, и удовольствие от достижения цели отсутствует? Это происходит тогда, когда не были зафиксированы критерии достижения цели. Когда в процессе деятельности понимание цели видоизменилось, желание рассеялось, и возможно, цель вообще прошла мимо первоначальной её постановки. Это расстраивает, демотивирует. Потому что человеку крайне необходимо созерцать результаты своей работы, которые приводят к выбросу эндорфинов и мотивируют двигаться дальше (Что создаёт нам хорошие ощущения от работы).

И это именно то, что означает литера M в аббревиатуре S.M.A.R.T. постановке целей.

Но есть путь, который позволит избежать этой ловушки — Test-First Thinking. Не задавайтесь вопросом об имплементации. Спросите себя “Как я смогу кому-то продемонстрировать выполненную задачу?”, “Как я могу протестировать, что все выполнено правильно?”, “Как я узнаю тот момент, когда работа сделана?”. Вопросы такого типа провоцируют дополнительные мыслительные цепочки, которые позволят схватить нюансы, которые обычно теряются при мыслях только о реализации. Это поможет отделить зерна от плевел и более четко определить, что на самом деле нужно, а что сейчас избыточно. Это сместит фокус с написания кода на достижение результата, что в конечном счете и приводит чувству удовлетворения.

Понятная задача

Если перед человеком стоит задача Важная и задача Срочная, какую он начнет делать?
Какую обычно выбираете вы?

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

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

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

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

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

Правильное наименование

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

1. Тест должен звучать как ответ на вопрос “Что делает” в полной формулировке, как будто задача уже сделана и является спецификацией для другого человека. Ведь если отвлечься, то мозг выпадет из контекста и понять, что надо сделать, уже тяжелее;

2. Название теста должно начинаться с глагола.

Пример получше:

describe(‘Функция factorial’, () => {
  it(‘возвращает 0 при отрицательном входном параметре’, () => {
    …
  })
})

Пример похуже:

describe(‘factorial’, () => {
  it(‘проверка на 0’, () => {
    …
  })
})

Эти правила опять же из описания GTD. Конкретно я об это почерпнул из Джедайских техник М. Дорофеева (Глава 3).

Прерывания

Сейчас (и уже давно) принято считать и громогласно говорить о высокой стоимости прерывания разработчика от рабочего процесса. Обычно речь идет о воздействии менеджеров на мыслительный процесс программиста. Например, THIS IS WHY YOU SHOULDN’T INTERRUPT A PROGRAMMER и The Cost of Interruption for Software Developers.

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

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

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

Научение через обратную связь

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

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

Тест coverage

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

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

Рефакторинг

Я не буду уделять много внимания на вполне понятный профит для рефакторинга при наличии качественных автотестов (Начинаем писать тесты (правильно) — Кирилл Мокевнин [Хекслет]). Это действительно сильно уменьшает дискомфорт и страх и позволяет разработчику более удобно и легко перелопачивать уже написанный код. Но про это говорится почти всегда, когда речь заходит про TDD, и, честно говоря, в контексте рефакторинга я не вижу большой разницы между TDD и тестами, написанными после имплементации.

Дисциплинированный разработчик

На мой субъективный взгляд, самая ключевая ценность TDD в том, что при его использовании разработчик неосознанно использует классические приемы самоуправления применительно к процессу написания кода. А это, определенно, требует дисциплины. Конечно, любой может быть организованным в работе, вне зависимости от использования TDD. Но тот, кто использует TDD в правильной интерпретации, автоматически будет организованным и дисциплинированным хотя бы применительно к написанию кода. Я считаю это очень важной характеристикой в текущее время печенек и PS-ок в офисе (до 2020) и удаленной работы в 2020.

Минусы TDD

Простите, но в контексте вышеописанного я их не вижу.

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

  1. Разный формат мышления при подходе к реализации задачи. Часть предпочитает строить верхний слой полного решения, а потом спускаться на нижние уровни и к детальной имплементации мелких функций, держа в голове весь алгоритм для всех кейсов. Если вы относите себя к таким людям, то, вероятно, TDD вам с первых попыток не понравится.
    Но, лично в моем случае, такой подход является контр-продуктивным. Мне просто не хватает силы сознания держать в голове так много требований, ветвлений алгоритма и самих переменных/объектов, что приводит к ошибкам и плохому решению. Обдумывая верхний уровень, я, если не ленюсь, то не код пишу, а выписываю/рисую список шагов, прикидываю, что я мог забыть, и определяю ближайшие шаги.

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

Итог

Да никакого итога. Мысли вслух. Скомпоновал как смог.
Если кто-то нашел в этой статье пищу для размышлений, то я доволен. Стремление к горстке признания это ведь так по-человечески.

P.S. (добавлено)

В комментариях заметил классический холивар на тему технических/практических плюсов/минусов TDD. Мой любимый холивар на эту тему тут - https://habr.com/ru/company/jugru/blog/313514/. Очень рекомендую.
Но в статье речь шла про ментальные приемы, чтобы поделиться новым взглядом на этот подход. О них и хотелось поговорить в комментариях. Спасибо.

15.12.2020 - обновил раздел "Мои первые шаги в TDD", убрав избыточные детали, сбивавшие с контекста.

Ads
AdBlock has stolen the banner, but banners are not teeth — they will be back

More

Comments 93

    +5
    Минусы TDD


    TDD и есть тот минус.
      +3

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

        0
        Спасибо. Справедливо.
        Перефразировал, подчеркнув акцент на контр-продуктивности персонально для себя.
          0

          Смысл тестов резко снижается, если они написаны не по top-down. Это как раз то, почему е2е тесты лучше чем юнит. Не платит никто за юнит тесты, платят за работающий софт)
          При рефакторинге все эти тесты приходится переписывать, потому что они были никому не нужны, кроме программиста пишущего код.

        +9

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

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

              +4

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

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

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

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

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

                      +1

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

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

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

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

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

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

                            Но так никто не делал.
                              0
                              Собственно статья о том, что они примерно так и делали. Только ссылки из статьи протухли. Гуглятся по названию:
                              A Longitudinal Study of the Use of a Test-Driven Development Practice in
                              Industry

                              nagappan_tdd.pdf
                              ITEA-AGILE-D2.7_v1.0
                                0
                                Вся суть тут
                                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 раз больше багов? Команда обезьян? Статья полная чушь, данные взяты с потолка.
                                  0

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

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


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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                                Справедливости ради, тесты очень помогают в апдейте версий библиотек. Вы увеличили версию, скомпилировали, прогнали тесты. Зеленые? Можно в прод. А если нельзя, значит ваши тесты не драйвят, а тормозят разработку

                                +2
                                никто не видел, чтоб это работало


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

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

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

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

                                    Поделитесь рецептом успеха? А то у меня не работало — от интеграционных тестов польза была, а юнит-тесты просто некуда было приспособить.

                                      0
                                      А что значит «некуда было приспособить»? В моем случае успех достигался простым применением методологии при решении новых задач. Безусловно, могут возникнуть проблемы при внедрении модульного тестирования в неприспособленный для этого код. Но у нас, как я говорил, модульные тесты уже по факту были, хоть и не выполняли 80% своих функций.
                                        0

                                        Ну, я просто не всегда понимаю, что именно юнит-тестировать.


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


                                        1. Сначала пишем тесты на парсер исходного языка — что те или иные выражения парсятся в то или иное AST. Отдельные функции не тестируются, тестируется весь парсер целиком.
                                        2. Потом пишем тесты на тайпчекер исходного языка — что те или иные выражения принимаются или отвергаются тайпчекером. Опять же, отдельные функции не тестируются, тестируется только тайпчекер целиком (и, более того, в тестах входными данными записаны строки с выражениями на языке — то есть, эти тесты используют и парсер).
                                        3. Потом пишем тесты на кодогенератор — что те или иные выражения после компиляции ведут себя так или иначе. Аналогично, отдельные функции не тестируются, и в этом случае уже задействуется весь пайплайн, от парсера и тайпчекера до генератора.

                                        Выглядит всё это в итоге как-то так.


                                        Это же не юнит-тесты? Ведь там не тестируются отдельные функции. А как и зачем тестировать отдельные функции в этом случае — непонятно.

                                          +1
                                          Это же не юнит-тесты? Ведь там не тестируются отдельные функции. А как и зачем тестировать отдельные функции в этом случае — непонятно.

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

                                            0

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


                                            Могут ли в Haskell быть такие кейсы, хотя бы чисто теоретически?

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

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

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

                                                0
                                                Но не те тесты, что написаны по TDD красными изначально, чтобы была причина написать код.

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

                                                  0

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


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


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

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

                                              Основной вопрос в том, функция ли это или модуль. С модулями понятно, та штука выше как раз модули (и экспортируемые из каждого из них 2-3 основных функции) и тестирует. Но, по идее, юнит-тесты — это ж на уровне отдельных функций, разве нет?

                                                +1

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

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

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

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

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


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

                                                        0

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


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

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

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

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

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


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


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


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

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

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

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

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

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

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

                                                    0

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

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

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

                                                        0

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

                                                          +1

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

                                                            0

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

                                                              0

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

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

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

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

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

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

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

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

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


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

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


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


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

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

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


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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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


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

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


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

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

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

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

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

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

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

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

                                                                        0

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

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

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

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

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

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


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


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

                                                                  0

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

                                                                    0

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

                                                                      0

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


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

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

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

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

                                                                    +1

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


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

                                                                      0

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

                                                                        0

                                                                        Моя главная претензия к тдд — это полный игнор к доменной модели. Об этом писал Скотт Влащин в своём блоге
                                                                        https://fsharpforfunandprofit.com/posts/roman-numeral-kata/


                                                                        Вместо математики и логики за кодом появляется мешанина спагетти кода

                                                                          0

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

                                                                        Only users with full accounts can post comments. Log in, please.