Вик Ву (Vic Wu) удалось схематизировать суть этой статьи.

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

Этот текст — мой ответ на комментарии в духе «TDD — лажа, потому что <что-то, что не TDD>», частый пример: «… потому что я ненавижу писать все тесты до того, как напишу код». Если вы что-то критикуете — критикуйте по делу.
  1. Напишите список тестовых сценариев, которые вы хотите покрыть.
  2. Превратите ровно один пункт из списка в реальный, конкретный, выполнимый тест.
  3. Измените код так, чтобы тест (и все предыдущие тесты) прошёл (добавляя пункты в список по мере их обнаружения).
  4. По желанию проводите рефакторинг, чтобы улучшить план реализации.
  5. Возвращайтесь к пункту #2, пока список не закончится.


Введение


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

Если вы делаете что-то не так, как описано ниже, и это работает — поздравляю! Да, это не Canon TDD, но кого это волнует? За точное следование этим шагам премию никому не выпишут.

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

Я просто стараюсь в целом быть позитивным и конструктивным. Этот пост из необходимости будет кратким и негативным в духе «Вы понимаете это неправильно. Вот как дело обстоят на самом деле». Я не собираюсь критиковать чей-то рабочий процесс, но хочу отточить ваше понимание Canon TDD.

Обзор


Разработка через тестирование (англ. test-driven development, TDD) — это рабочий процесс программирования. Программисту необходимо изменить поведение системы (которая может быть пустой в данный момент). TDD призван помочь программисту создать новое состояние системы, в котором:
  • Всё, что работало раньше, продолжает работать.
  • Новое поведение работает так, как ожидалось.
  • Система готова к следующим изменениям.
  • Программист и его коллеги чувствуют уверенность в вышеуказанных пунктах.


Разделение интерфейса и реализации


Первый момент недопонимания заключается в том, что люди, похоже, объединяют всю архитектуру вместе. Существует две её разновидности:
  • Как вызывается определенная часть поведения.
  • Как система реализует это поведение.

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

Шаги


Люди — так себе компьютеры. То, что написано ниже, похоже на компьютерную программу, но это не так. Она написана таким образом в попытке эффективно общаться с людьми, которые привыкли работать с программами. Я говорю «попытка», потому что, как отмечалось выше, люди склонны говорить: «TDD — это полная чушь. Я сделал <что-то совсем другое> и у меня ничего не вышло.

1. Список тестов


Первоначальным шагом в TDD, при наличии системы и желаемого изменения в поведении, является составление списка всех ожидаемых вариантов нового поведения. «Вот основной вариант / а это если сервис выйдет из строя / а это если ключа ещё нет в базе данных и так далее».

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

Ошибка: примешивание решений по архитектуре реализации. Расслабтесь. У вас будет достаточно времени позже, чтобы решить, как должна выглядеть внутренняя часть. Вы лучше справитесь с перечислением тестов, если сосредоточитесь только на этом. (Если вам нужен эскиз реализации, нарисованный маркерами Sharpie на салфетке, — вперёд, но на самом деле он может и не понадобиться. Экспериментируйте).

Похоже, что читатели пропустил этот шаг в книге. «TDD просто переходит в кодирование. Вы никогда не узнаете, когда закончите». Неа.

2. Напишите тест


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

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

Ошибка: преобразовывать все пункты списка тестов в конкретные тесты, а затем запускать их по одному. Что происходит, когда прохождение первого теста заставляет вас пересмотреть решение, которое влияет на все эти отвлечённые тесты? Работа над ошибками. Что случится, если вы дойдёте до теста № 6 и не увидите, что хоть что-то прошло? Депрессия и/или скука.

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

3. Сделать так, чтобы тест прошёл


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

Ошибка: удалять утверждения и притворяться, что тест прошёл. Сделайте так, что он прошёл по-настоящему.

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

Ошибка: смешивать рефакторинг с выполнением теста. Снова проблема «ношения двух шляп». Сделайте так, чтобы заработало, а затем — чтобы было правильно написано (отсылка к принципу экстремального программирования Кента Бека: “Make it run, then make it right”). Ваш мозг со временем скажет вам спасибо.

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

4. Рефакторинг по желанию


Теперь вам предстоит принимать решения по плану реализации.

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

5. Возвращайтесь к пункту 2 до тех пор, пока список тестов не опустеет


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

Больше практических навыков по тестированию приложений вы можете получить в рамках практических онлайн-курсов от экспертов отрасли.

Комментарии (1)


  1. kenomimi
    18.07.2024 12:28

    TDD в целом хорошо, но дорого и геморно.

    Во-первых. в любых тестах надо следить за консистеностью тестовых данных - что боль для разработчика, ибо работа превращается в бесконечное нудное выверение экселек (json, xml, ... - на выбор). Поменял логику на одно ветвление - побежал три дня править бесконечные простыни датасетов, после чего хочется убивать.

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