Начальная реализация разработки на основе тестирования

#tdd

#tdd

Вопрос:

Обычная практика TDD заключается в том, что вы делаете крошечные шаги. Но одна вещь, которая меня беспокоит, — это то, что я видел, как делают несколько человек, где они просто жестко кодируют значения / параметры, а затем проводят рефакторинг позже, чтобы заставить его работать должным образом. Например…

 describe Calculator
  it should multiply
    assert Calculator.multiply(4, 2) == 8
  

Затем вы делаете как можно меньше, чтобы это прошло:

 class Calculator
  def self.multiply(a, b)
    return 8
  

И это так!

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

Комментарии:

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

Ответ №1:

Эта практика известна как «Подделывай, пока не сделаешь это». Другими словами, вводи поддельные реализации до тех пор, пока не станет проще внедрить реальную реализацию. Вы спрашиваете, почему мы это делаем.

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

Еще одна причина для этой практики — поддерживать быстрый ритм красного / зеленого / рефакторинга. Это сердцебиение, которое управляет TDD, и важно, чтобы у него был быстрый цикл. Важно, чтобы вы чувствовали прогресс, важно, чтобы вы знали, где вы находитесь. Некоторые проблемы (очевидно, не эту) нельзя решить в одно мгновение, но мы должны продвигаться к ним в одно мгновение. Притворяйся, пока не сделаешь это, — это способ обеспечить своевременный прогресс. Смотрите также flow.

Комментарии:

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

Ответ №2:

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

Кроме того, религиозная приверженность TDD подскажет вам, что не должно быть никакой логики, в которой не указано требование, проверенное утверждением в модульном тестировании. Показательный пример; на данный момент единственным тестом для умножения в системе является утверждение, что ответ должен быть 8. Таким образом, на данный момент ответ ВСЕГДА равен 8, потому что требования не говорят вам ничего другого.

Это кажется очень строгим и в контексте такого простого случая, как этот, бессмысленным; чтобы проверить правильность функциональности в общем случае, вам потребуется бесконечное количество модульных тестов, когда вы, как разумный человек, «знаете», как должно работать умножение, и могли бы легко настроить тест, который сгенерировал и протестировал таблицу умножения до некоторого предела, что дало бы вам уверенность, что она будет работать во всех необходимых случаях. Однако в более сложных сценариях с более задействованными алгоритмами это становится полезным исследованием преимуществ YAGNI. Если в требовании указано, что вам необходимо иметь возможность сохранять запись A в БД, а возможность сохранения записи B опущена, то вы должны сделать вывод, что возможность сохранения записи B вам не понадобится, пока не появится требование, в котором указано это. Если вы реализуете возможность сохранения записи B до того, как узнаете, что вам это нужно, то, если окажется, что вам это никогда не понадобится, вы потратили время и усилия на ее внедрение в систему; у вас есть код без бизнес-цели, который, несмотря на это, все равно может «сломать» вашу систему и, следовательно, требует обслуживания.

Даже в самых простых случаях вы можете в конечном итоге закодировать больше, чем вам нужно, если будете кодировать сверх требований, которые, как вы «знаете», слишком легки или специфичны. Допустим, вы внедряли какой-то анализатор для строковых кодов. В требованиях указано, что строковый код «AA» = 1, а «AB» = 2, и это предел требований. Но вы знаете, что полная библиотека кодов в этой системе включает в себя 20 других, поэтому вы включаете логику и тесты, которые анализируют полную библиотеку. Вы возвращаетесь к клиенту, ожидая оплаты за время и материалы, а клиент говорит: «Мы об этом не просили; мы используем только два кода, которые указали в тестах, поэтому мы не платим вам за дополнительную работу». И они были бы совершенно правы; технически вы пытались обмануть их, взимая плату за код, который они не просили и в котором не нуждаются.