Каков наилучший способ действий в TDD, если после правильной реализации логики тест по-прежнему не проходит (потому что в тесте есть ошибка)?
Например, предположим, что вы хотели бы разработать следующую функцию:
int add(int a, int b) {
return a + b;
}
Предположим, мы разработали его в следующие шаги:
Написать тест (пока нет функции):
// test1 Assert.assertEquals(5, add(2, 3));
Приводит к ошибке компиляции.
Напишите фиктивную реализацию функции:
int add(int a, int b) { return 5; }
Результат:
test1
проходит.Добавьте еще один тест:
// test2 -- notice the wrong expected value (should be 11)! Assert.assertEquals(12, add(5, 6));
Результат:
test2
не удается,test1
все еще проходит.Напишите реальную реализацию:
int add(int a, int b) { return a + b; }
Результат:
test1
все еще проходит,test2
все еще терпит неудачу (так как11 != 12
).
В данном конкретном случае: было бы лучше:
- исправить
test2
и посмотреть, что сейчас проходит, или - удалите новую часть реализации (т. е. вернитесь к шагу № 2, приведенному выше), исправьте
test2
и оставьте ее неудачной, а затем снова введите правильную реализацию (шаг № 4 выше).
Или есть какой-то другой, более умный способ?
Хотя я понимаю, что примерная проблема довольно тривиальна, меня интересует, что делать в общем случае, который может быть более сложным, чем сложение двух чисел.
РЕДАКТИРОВАТЬ (в ответ на ответ @Thomas Junk):
Основное внимание в этом вопросе уделяется тому, что TDD предлагает в таком случае, а не тому, что является «универсальной передовой практикой» для достижения хорошего кода или тестов (которые могут отличаться от способа TDD).
Ответы:
Абсолютно критическим моментом является то, что вы видите, что тест прошел и не прошел
Независимо от того, удаляете ли вы код, чтобы сделать тест неудачным, затем переписать код или перенести его в буфер обмена, чтобы потом вставить его позже, не имеет значения. TDD никогда не говорил, что ты должен что-то перепечатывать. Он хочет знать, что тест проходит только тогда, когда он должен пройти, и терпит неудачу, только когда он должен провалиться.
Наблюдение за прохождением теста и его провалом - это то, как вы тестируете тест. Никогда не доверяйте тесту, который вы никогда не видели, и то, и другое.
Рефакторинг против красной полосы дает нам формальные шаги для рефакторинга рабочего теста:
Однако мы не проводим рефакторинг рабочего теста. Мы должны преобразовать тест с ошибками. Одной из проблем является код, который был введен в то время, когда его охватывал только этот тест. Такой код следует откатить и снова ввести после исправления теста.
Если это не так, и покрытие кода не является проблемой из-за других тестов, охватывающих код, вы можете преобразовать тест и представить его как зеленый тест.
Здесь также выполняется откат кода, но этого достаточно, чтобы тест не прошел. Если этого недостаточно для охвата всего кода, введенного в то время, когда он охватывается только тестом с ошибками, нам требуется больший откат кода и больше тестов.
Введите зеленый тест
Нарушение кода может быть закомментированием кода или перемещением его в другое место, только чтобы вставить его позже. Это показывает нам объем кода, который охватывает тест.
Для этих последних двух запусков вы вернетесь в нормальный красно-зеленый цикл. Вы просто вставляете вместо того, чтобы печатать, чтобы не нарушать код и проходить тест. Поэтому убедитесь, что вы вставляете достаточно только для прохождения теста.
Общая картина здесь - видеть, как цвет теста меняется так, как мы ожидаем. Обратите внимание, что это создает ситуацию, когда вы кратко проходите ненадежный зеленый тест. Будьте осторожны, чтобы прервать вас и забыть, где вы находитесь на этих шагах.
Мой благодаря Rubberduck для Охватывая Красной Bar ссылки.
источник
Какую общую цель вы хотите достичь?
Делать хорошие тесты?
Делаете правильную реализацию?
Делать TTD религиозно правильно ?
Ни один из вышеперечисленных?
Возможно, вы переосмыслите свое отношение к тестам и тестированию.
Тесты не дают никаких гарантий о правильности реализации. Прохождение всех тестов ничего не говорит о том, делает ли ваше программное обеспечение то, что должно; он не делает никаких существенных заявлений о вашем программном обеспечении.
Принимая ваш пример:
«Правильная» реализация дополнения будет эквивалентна коду
a+b
. И пока ваш код делает это, вы бы сказали, что алгоритм верен в том, что он делает, и он правильно реализован.На первый взгляд , мы оба согласились бы, что это является реализация дополнения.
Но то, что мы делаем, на самом деле не говорит о том, что этот код является его реализацией,
addition
он ведет себя только в определенной степени как единое целое : подумайте о целочисленном переполнении .Целочисленное переполнение происходит в коде, но не в концепции
addition
. Итак: ваш код ведет себя в определенной степени как концепцияaddition
, но это не такaddition
.Эта довольно философская точка зрения имеет несколько последствий.
И можно сказать, что тесты - это не более чем предположения об ожидаемом поведении вашего кода. При тестировании кода, вы могли бы (возможно) никогда не убедитесь , что ваша реализация является право , то лучшее , что вы могли бы сказать, что ваши ожидания относительно того , что результаты ваш код поставляет были или не были удовлетворены; будь то, что ваш код неправильный, будь то, что ваш тест не так или будь то, что оба они не правы.
Полезные тесты помогут вам исправить ваши ожидания в отношении того, что должен делать код: пока я не изменяю свои ожидания и пока измененный код дает мне ожидаемый результат, я могу быть уверен, что предположения, которые я сделал относительно результаты, кажется, удаются.
Это не помогает, когда вы сделали неправильные предположения; но эй! по крайней мере, это предотвращает шизофрению: ожидание других результатов, когда их не должно быть.
ТЛ; др
Ваши тесты - это предположения о поведении кода. Если у вас есть веские основания полагать, что ваша реализация верна, исправьте тест и посмотрите, выполняется ли это предположение.
источник
datatype
это явно неправильный выбор. Тест показал бы, что: ваше ожидание будет «работает для больших чисел» и в некоторых случаях не будет выполнено. Тогда вопрос будет в том, как бороться с этими случаями. Это угловые дела? Когда да, как с ними бороться? Возможно, некоторые оговорки, связанные с квардом, помогают предотвратить беспорядок. Ответ связан с контекстом.Вы должны знать, что тест будет неуспешным, если реализация ошибочна, что не то же самое, что проходить, если реализация верна. Поэтому вы должны вернуть код в состояние, в котором вы ожидаете, что он потерпит неудачу, прежде чем исправлять тест, и убедиться, что он потерпел неудачу по той причине, которую вы ожидали (т.е.
5 != 12
), а не что-то еще, что вы не предсказывали.источник
assertTrue(5 == add(2, 3))
дает менее полезный вывод, чемassertEqual(5, add(2, 3))
хотя они оба тестируют одно и то же).В этом конкретном случае, если вы измените 12 на 11, и теперь тест пройден, я думаю, вы хорошо поработали как над тестом, так и над его реализацией, поэтому не нужно много проходить через дополнительные циклы.
Однако та же проблема может возникнуть в более сложных ситуациях, например, когда у вас есть ошибка в вашем установочном коде. В этом случае, после исправления теста, вы, вероятно, должны попытаться изменить свою реализацию таким образом, чтобы этот конкретный тест не прошел, а затем отменить мутацию. Если отмена реализации является самым простым способом сделать это, то это нормально. В вашем примере, вы можете мутировать
a + b
вa + a
илиa * b
.В качестве альтернативы, если вы можете слегка изменить утверждение и увидеть, что тест не пройден, это может быть довольно эффективно при тестировании теста.
источник
Я бы сказал, это случай для вашей любимой системы контроля версий:
Проведите исправление теста, сохранив изменения кода в рабочем каталоге.
Подтвердить с соответствующим сообщением
Fixed test ... to expect correct output
.При
git
этом для этого может потребоваться использование,git add -p
если тест и реализация находятся в одном и том же файле, в противном случае вы, очевидно, можете просто разделить два файла по отдельности.Зафиксируйте код реализации.
Вернитесь во времени, чтобы проверить фиксацию, сделанную на шаге 1, и убедитесь, что тест на самом деле провалился .
Вы видите, что таким образом вы не полагаетесь на свое мастерство редактирования, чтобы убрать свой код реализации с пути, пока вы тестируете свой провальный тест. Вы используете свою VCS, чтобы сохранить свою работу и убедиться, что записанная история VCS правильно включает в себя как провал, так и прохождение теста.
источник