Может ли методология TDD применяться сверху вниз?


13

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

assert mergesort([]) === []

и тест не проходит с

NameError: имя 'mergesort' не определено

Я тогда добавляю

def mergesort(a):
    return []

и мой тест проходит. Далее я добавляю

assert mergesort[5] == 5

и мой тест не проходит с

AssertionError

с которым я делаю пас

def mergesort(a):
    if not a:
        return []
    else:
        return a

Далее я добавляю

assert mergesort([10, 30, 20]) == [10, 20, 30]

и теперь я должен попытаться сделать это. Я «знаю» алгоритм слияния, поэтому пишу:

def mergesort(a):
    if not a:
        return []
    else:
        left, right = a[:len(a)//2], a[len(a)//2:]
        return merge(mergesort(left)), mergesort(right))

И это не с

NameError: имя 'merge' не определено

Теперь вот вопрос. Как я могу убежать и начать mergeиспользовать TDD? Кажется, что я не могу, потому что у меня есть этот «зависший» невыполненный, не пройденный тест mergesort, который не пройдет, пока mergeне закончится!Если этот тест зависает, я никогда не смогу сделать TDD, потому что я не буду «зеленым» во время построения итераций TDD merge.

Кажется, что я застрял в следующих трех уродливых сценариях и хотел бы знать (1) какой из них предпочитает сообщество TDD, или (2) есть другой подход, который мне не хватает? Я смотрел несколько прохождений дяди Боба TDD и не помню, чтобы видел такой случай раньше!

Вот 3 случая:

  1. Реализуйте слияние в другом каталоге с другим набором тестов.
  2. Не беспокойтесь о том, чтобы быть зеленым при разработке вспомогательной функции, просто следите за тем, какие тесты вам действительно нужны. хотите пройти.
  3. Закомментируйте (GASP!) Или удалите строки в mergesortэтом вызове merge; затем, после mergeработы, положите их обратно.

Все это выглядит глупо для меня (или я смотрю на это неправильно?). Кто-нибудь знает предпочтительный подход?


2
Часть цели TDD - помочь вам создать дизайн программного обеспечения. Частью этого процесса проектирования является обнаружение того, что необходимо для достижения желаемого результата. В случае mergesort, так как это уже очень четко определенный алгоритм, этот процесс обнаружения не требуется, и тогда он становится вопросом сопоставления того, что вы уже знаете, как дизайна, с серией модульных тестов. Предположительно, ваш тест верхнего уровня утверждает, что ваш тестируемый метод принимает несортированную коллекцию и возвращает отсортированную ...
Роберт Харви

1
... Последующие юнит-тесты будут постепенно углубляться в реальную механику а mergesort. Если вы ищете «правильный» способ сделать это, то нет другого, кроме как быть точным в отношении сопоставления mergesortалгоритма с серией юнит-тестов; то есть они должны отражать то, что на mergesortсамом деле делает.
Роберт Харви

4
Дизайн не вырастает из одних только юнит-тестов; если вы ожидаете, что mergesortдизайн появится естественным образом из красно-зеленого рефактора, этого не произойдет, если вы не будете руководить процессом на основе имеющихся у вас знаний mergesort.
Роберт Харви


1
В TDD mergeдолжны быть изобретены только на стадии «рефакторинга». Если вы видите, что mergeметод может быть введен для прохождения теста mergesort, сначала сделайте тесты без mergeметода. Затем рефакторинг вашей реализации путем введения mergeметода.
Фабио

Ответы:


13

Вот несколько альтернативных способов взглянуть на ваши варианты. Но сначала правила TDD, от дяди Боба с акцентом на меня:

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

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

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

Другое объяснение состоит в том, что вы пишете сортировку слиянием, вы знаете, что вам понадобится mergeоперация (т. Е. Это не YAGNI, которую пытается «сократить» правило «достаточного»). Следовательно, вы должны были начать с тестов на слияние, и только затем приступить к тестам для общей сортировки.


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

@RayToal - я действительно склоняюсь к подходу полного тестирования mergeоперации перед выполнением сортировки (а также отдельного тестирования partitionоперации). Я думаю, что заявленное преимущество возникающего дизайна происходит от медленной работы по достижению известной цели. В случае сортировки слиянием, я не думаю, что целью является сортировка в целом (потому что тогда у вас будет пузырьковая сортировка). Вы знаете основные операции, поэтому вы работаете над этими операциями; вид в основном запоздалая мысль.
kdgregory

1
Есть четвертый вариант. Передайте mergeфункцию mergesortи смоделируйте ее поведение. Затем вернитесь и выполните mergeтест в первую очередь. Делегаты потрясающие ™.
RubberDuck

@RubberDuck Мокание неотъемлемой части домена ядра может привести к некоторым проблемам, более конкретно, когда вы запускаете программу, а функции макетов и слияния отличаются наименьшей детализацией. Подобный подход следует оставить для случаев, когда вы работаете с внешними ресурсами, например, откуда берется список для сортировки.
Кламач
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.