Только в теории

29

Чуть больше года назад мне посчастливилось взять 9-месячный перерыв в работе. Я решил, что в то время я буду оттачивать свои навыки C #. Я начал работать над кучей проектов и заставил себя следовать TDD.

Это был довольно поучительный процесс.

Сначала это было непросто, но со временем я научился писать более тестируемый код (который, как выясняется, обычно является более твердым кодом), и в процессе я также оттачивал свои навыки проектирования ОО.

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

Я предпочитаю не следовать TDD.

Я считаю, что TDD замедляет меня и фактически усложняет разработку чистого приложения.

Вместо этого я принял немного (очень) другой подход:

  1. Выберите вертикальный кусок работы
  2. Разработать функционирующий прототип
  3. Рефакторинг пока все хорошо и аккуратно
  4. Расслабьтесь и оцените красивый твердый и тестируемый код, который я написал.

Вы, возможно, заметили, что шаг 1 не был «определите публичную поверхность моей тестовой цели», а шаг 2 не был «тестом bejesus вне указанной публичной поверхности». Возможно, вы также заметили, что ни один из шагов не включает тестирование. Я пишу тестируемый код, но я не тестирую его ... пока что.

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

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

TDD в теории. Не на практике.

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

  1. Реализуйте рабочий вертикальный фрагмент работы с учетом тестирования, но не пишите никаких тестов.
  2. Если в будущем (например, через месяц) этот срез нуждается в модификации
    1. Написать модульные тесты, интеграционные тесты, поведенческие тесты и т. Д., Которые гарантируют правильность работы
    2. Изменить код
  3. Если этот срез не нуждается в модификации,
    1. Ничего не делать

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

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

Итак, я иду в ад [BT] DD, или это обычная форма прагматического кодирования и тестирования?

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

Заметка:

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

MetaFight
источник
2
Выглядит как спайк и стабилизируется, не выполняя стабилизирующую функцию, если If that slice doesn't need modification. lizkeogh.com/2012/06/24/beyond-test-driven-development
RubberChickenLeader
13
Вы открыли для себя кое-что о TDD, о котором я давно подозревал, что тестовая первая мантра является исключительно хорошим инструментом обучения, но это не дизайн, а просто поощрение хорошего дизайна. В конце концов, вам нужны тестируемый код и модульные тесты, которые обеспечивают хорошее покрытие кода и отражают требования программного обеспечения; как вы узнаете, вы можете получить это без предварительного написания тестов , если будете практиковать разумные принципы проектирования.
Роберт Харви
5
Да, и написание тестов в первую очередь удваивает вашу работу по созданию прототипов.
Роберт Харви
3
это означает, что я лгал о том, что это "слегка".
MetaFight
1
«И когда я приступаю к написанию тестов, я пишу гораздо меньше из них, но покрываю почти столько же (более высокий ROI)». Когда вы говорите, пишите гораздо меньше из них, вы просто имеете в виду, потому что тестируете только код меняется, или вы говорите, что каким-то образом покрываете один и тот же (проверенный) фрагмент кода меньшим количеством тестов, чем если бы вы использовали TDD?
Бен Ааронсон

Ответы:

6

Чтобы процесс работал в долгосрочной перспективе, я писал бы тесты во время написания кода.

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

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

Если оставить это до позднего времени, знание (естественно) со временем уменьшится.

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

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

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

Майкл Даррант
источник
Я думаю, что мне нравится ваш предложенный подход, и я, вероятно, буду применять его, когда смогу. Моя работа очень фрагментирована, поэтому «временная шкала часов» не всегда возможна. К сожалению, я тоже на поддержке, поэтому меня часто отвлекают от работы, чтобы помочь бороться с пожарами :) Но это жизнь.
MetaFight
Проблема в том, что завтра никогда не наступит, всегда есть следующая особенность. И что ты собираешься делать? написать следующую функцию или написать тесты для того, что вы только что «закончили»?
Энди
9

Хотя TDD сложно реализовать на 100%, в вашем подходе есть недостаток

  1. Реализация рабочего вертикального среза работы

    1.1 проходит 1 год ....

    1.2 Новый разработчик начинает работу над проектом

  2. Если этот срез нуждается в модификации

    2.3 Разбор имен методов и параметров стиля «Чистое кодирование» «GetUnicorn (colourOfUnicorn)»

    2.4 Прочитайте xml комментарии 'Получает золотого единорога (для верховой езды) (obvs)'

    2.5 Выследить оригинального разработчика

    2.6 Надеюсь, они помнят, что должен делать код

    2.7 Заставьте их объяснить все это

  3. Написать модульные тесты, интеграционные тесты, поведенческие тесты и т. Д., Которые , как мы надеемся, гарантируют, что часть работы верна

  4. Изменить код

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

Ewan
источник
2
Эй, я пишу самодокументированный код! Мои занятия несут одну ответственность, и поэтому их легко понять. Никто не должен будет выследить меня :)
MetaFight
7
@ MetaFight, и если они это сделают, вам будет легко заметить вершину живого единорога из чистого золота!
Jonrsharpe
3
Я зову его Голдикорн.
MetaFight,
На более серьезной ноте, да, у вас есть точка зрения. Я подумал о том, чтобы регистрировать истории «тестирования долга», чтобы я мог окупить их, когда моя рабочая нагрузка станет меньше.
MetaFight,
4
Если код написан хорошо, но имеет ошибку, вероятно, будет довольно легко понять, что первоначальный разработчик хотел достичь, читая код, и новый разработчик может добавить необходимые тесты. Единственная проблема заключается в том, что большинство разработчиков думают, что они пишут хороший код. «Хорошо» во многом зависит от вашей точки зрения и опыта работы программистом. Итак, в этом есть напряжение, которым нужно управлять.
Фил
4

Я согласен как с Дэниелом Холлинрейком, так и с Юаном, что первый ключевой момент, почему ваш тест «только для изменения» работает хорошо:

I am the sole developer on my projects and I am responsible for everything

и что вероятным вторым ключевым моментом является:

you're producing nice clean code

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

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

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

sergut
источник
1
Я думаю, что часть проблемы заключается в том, что может быть только 1 программист, но база кода часто увеличивается со временем, и то, что работало (для тестирования), когда оно было маленьким, не продолжало работать, поскольку оно становилось больше.
Майкл Даррант
3

Для меня ключевым моментом является следующее:

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

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

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

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

Даниэль Холлинрейк
источник
Я бы сказал, что набор тестов будет на 4-м месте для передачи другим разработчикам - документация по требованиям, архитектурная схема и документация по проектированию будет гораздо важнее для обсуждения вопросов, чем набор модульных тестов.
gbjbaanb
Это справедливо, но, к сожалению, по моему опыту, почти каждый проект, над которым я работал над документацией, когда он существует, либо устарел, либо не завершен.
Даниэль Холлинрейк,
1
То же самое и здесь, поэтому разработчики должны понимать важность документирования вещей, а не писать больше кода в виде тестов! Возможно, нам нужны инструменты, которые позволят лучше генерировать документацию (то есть это не просто форматирование сигнатур методов) из комментариев кода и заявок.
gbjbaanb
Я немного отредактировал свой ответ в ответ на ваши комментарии. Спасибо.
Даниэль Холлинрейк
1
@gbjbaanb Если я могу помочь, мне нравится избегать написания документов по требованиям, архитектурных схем и документов по дизайну. Это потому, что они очень быстро устаревают. В моем случае мне повезло, так как я управляю многими небольшими приложениями с очень небольшим количеством обязанностей. Это делает требования и документацию по архитектуре немного излишними. И проекты настолько малы, что общий дизайн понятен. Что я имею в документировании, однако, как системы взаимодействуют между собой , как развернуть их, и как следить за своим здоровьем.
MetaFight
3

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

Проблемы, которые я вижу в практике написания тестов непосредственно перед тем, как вы их измените:

Мне часто нужно вносить изменения в спешке

В то время как вы можете сэкономить время в целом, написав тесты только тогда, когда они вам нужны, не все время одинаково. Потратить 2 часа на написание тестов, чтобы сэкономить 1 час, когда я нахожусь в кризисном режиме - это того стоит.

Проще писать тесты одновременно с написанием кода

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


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

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

Джастин
источник
2

Это хороший вопрос, и FWIW я добавлю два моих цента.

Около года назад я занимался кодированием в Salesforce, платформе, которая имела встроенный механизм, который заставлял вас не обязательно писать тесты перед написанием кода , а скорее заставлял вас писать тесты в целом.

Это работало так, что система заставляла вас писать тесты, и она вычисляла количество строк вашего кода, которые были протестированы, в процентах. Если весь код в вашем производственном экземпляре упал ниже 75% протестированных. Salesforce больше не работает.

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

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

Затем неловкая концепция TDD охватила наш отдел, вплоть до наших баз данных. Наши архитекторы хотели провести тщательное тестирование во всех аспектах нашего ИТ-отдела. Легкие боли в заднице, встречаются еще большие боли в заднице.

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

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

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

Канадский кодер
источник
2
Я чувствую, что мы находимся на расстоянии одного поколения от правильной разработки TDD. Я думаю, что сейчас TDD как бы «привязан» к большинству языков с фреймворками xUnit. В какой-то момент это будет просто встроено в кодирование, а не отдельно. Как вы бы определили класс, и сразу же будут созданы заглушки для всего тестирования вместе с некоторым подмножеством самих тестов (те, которые могут быть легко определены самими классами / методами).
Calphool
3
@Calphool Мы были на самом деле интегрированы некоторые легко проверяемые вещи на язык! Мы называем это статической типизацией . Rust идет дальше с проверкой заимствований, чтобы проверить еще больше ошибок. Но большинство тестов относятся именно к этому классу («если я нажму кнопку, виджет станет красным») - как компилятор / IDE узнает, что вы собираетесь это тестировать?
user253751
1
@immibis: Возможно, расширив проверку типов в дальнейшем. Возможно, концепция «виджет становится красным» становится концепцией первого класса, которую можно каким-то образом вывести из кода. Я не утверждаю, что у меня есть ответы, я просто чувствую, что TDD все еще достаточно нов, чтобы не полностью интегрироваться в эволюцию языка.
Calphool
1
Специалисты Salesforce, в частности, неправильно проводят тестирование: они требуют наличия тестов на месте, но смехотворно затрудняют написание тестов качества . Это звучит великолепно в теории, но на практике это заставляет разработчиков желать выбивать глаза ложками.
1

Я не мог бы порекомендовать ваш подход.

Если бы я использовал ваш подход, это было бы, например, следующим образом (дом - это приложение):

  1. Я начинаю строить дом для своей семьи как каменщик с некоторыми знаниями или как начинающий.
  2. Я знаю требования, такие как детские комнаты, комнаты для гостей и начинаю строить свой «прототип» дома.
  3. Спустя пару раз ваш дом-прототип готов.
  4. Я начинаю искать, достаточно ли стабильна структура вручную. Поэтому я поднял много гирь и перенес их в разные комнаты на первом этаже. Чтобы убедиться, что когда я сижу в комнате с семьей, потолок не сломается. Но это ломается, и я начинаю рефакторинг. Сначала очистить всю массу. Затем создайте новый и протестируйте его снова вручную, пока он не станет достаточно стабильным.
  5. Чем я перееду со своей семьей. Все хорошо.
  6. Через месяц мои кузены и родители приезжают к нам в гости. Но прежде чем они смогут войти в наш дом, им нужно заплатить архитектору и инженеру-строителю, чтобы убедиться, что потолок не сломается, когда мы сидим в одной из комнат на первом этаже.
  7. У архитектора и инженера-строителя много работы, потому что им не с чего начать. Поэтому им нужно пойти в мой дом и посмотреть, как я его строю.
  8. И снова это не достаточно стабильно. Поэтому им приходится проводить рефакторинг земли первого этажа.
  9. Но после этого все хорошо, и все могут спокойно войти в мой дом.

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

Таким образом, есть лучший подход без программирования «прототипа» и чем начинать рефакторинг. Вместо программирования прототипа "сделайте дизайн с использованием UML вашего приложения следующим образом.

  1. Создайте диаграмму UseCase. Вы можете использовать draw.io для начала.
  2. Затем создайте диаграмму EPK на основе ваших прецедентов, чтобы определить поведение. (ПОВЕДЕНИЕ вашего приложения) Быстрее выполнить рефакторинг, чем рефакторинг закодированного прототипа. Особенно, когда вы начинающий.
  3. Создать диаграмму класса. (СТРУКТУРА вашего заявления)
  4. Определите, где вы можете получить проблемы при реализации поведения.
  5. Напишите для этого простой прототип с 10 или 20 строками кода, чтобы определить, как вы можете реализовать это поведение. Хорошо для начинающих. Или посмотрите учебник, посмотрите исходный код других примеров приложений. Как они это решили.
  6. Чем начать кодирование. Во-первых, успешные тесты вашего UseCase. Это можно сделать разными способами. Сначала создайте всю структуру, которая необходима для теста и этого UseCase. При использовании Enterprise Architekt структура может быть сгенерирована для вас. На основании ваших диаграмм. Или создайте структуру, пока проводите Тест. Так что никаких ошибок компиляции не появляется. Здесь упоминается, что вам ТОЛЬКО нужно проверить ПОВЕДЕНИЕ вашего приложения. Случаи использования у вас есть.
  7. Чем реализовать поведение вашего UseCase.
  8. После успешных вариантов использования начните писать тесты для исключений. И всегда хорошо, когда вы видите зеленые цвета, когда ваши тесты действительны;)
  9. И вы сделали.

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

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

Rocks360
источник
1

Код, который я пишу, работает. Это работает, потому что я проверяю это вручную.

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

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

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

  • Выберите вертикальный кусок работы

  • Разработать функционирующий прототип

  • Рефакторинг пока все хорошо и аккуратно

И снова: во время рефакторинга вы вручную тестировали всю логику, на которую влияет рефакторинг? Сколько времени занимает проверка на изменение рефакторинга? Если рефакторинг ломает какой-то код, как долго вы находите причину перерывов?

  • Расслабьтесь и оцените красивый твердый и тестируемый код, который я написал.

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

Вы нашли свой путь очень продуктивным только потому, что вы только разработчик, работающий с кодом, и, я думаю, потому что вы только начинаете работать в этом проекте (Сколько лет этому проекту вы работаете? 6 - 8 месяцев?).
Вы все еще помните все, что написали, и можете распознать причину возможных проблем. Я почти уверен, что вы начнете писать тесты с начала 2-3 года вашего проекта - потому что вы хотите быть уверены, что ничего не забудете.

Fabio
источник
0

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

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

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

JacquesB
источник
Конечно, хорошая архитектура, принципы SOLID и т. Д. Должны предотвратить это - нет. Сложные системы имеют части, которые влияют на другие части, это просто так. например, изменение грамматики Antlr в Rubberduck может легко заставить намеченную модифицированную деталь отлично работать, в то же время нарушая 45 других функций. Без тщательных тестов нет никакой возможности узнать это, и вам придется сойти с ума, чтобы каждый раз вручную проверять все случаи. Если я что-то изменяю в резольвере, и тесты 987 прерываются, я знаю, что сделал что-то не так и что это повлияло
Матье