Czy metodologię TDD można stosować odgórnie?

13

Nie jestem pewien, w jaki sposób TDD, metodologia, obsługuje następujący przypadek. Załóżmy, że chcę zaimplementować algorytm scalania w Pythonie. Zaczynam od pisania

assert mergesort([]) === []

a test kończy się niepowodzeniem

Nazwa Błąd: nazwa „scalanie” nie jest zdefiniowana

Następnie dodaję

def mergesort(a):
    return []

i mój test mija. Następnie dodaję

assert mergesort[5] == 5

i mój test kończy się niepowodzeniem

AssertionError

które przekazuję

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

Następnie dodaję

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

i teraz muszę spróbować to przekazać. „Znam” algorytm scalania, więc piszę:

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

I to się nie udaje

NameError: nazwa „merge” nie jest zdefiniowana

Oto pytanie. Jak mogę uciec i zacząć wdrażać mergeza pomocą TDD? Wygląda na to, że nie mogę, bo mam ten „wiszący” niespełniony, nieudany test mergesort, który nie przejdzie, dopóki się mergenie skończy! Jeśli ten test się zawiesi, nigdy tak naprawdę nie będę mógł wykonywać TDD, ponieważ nie będę „zielony” podczas konstruowania iteracji TDD merge.

Wygląda na to, że utknąłem w poniższych trzech brzydkich scenariuszach i chciałbym wiedzieć (1), który z nich preferuje społeczność TDD, lub (2) czy istnieje inne podejście, którego mi brakuje? Obejrzałem kilka instrukcji wujka Boba TDD i nie przypominam sobie, aby widziałem wcześniej taki przypadek!

Oto 3 przypadki:

  1. Zaimplementuj scalanie w innym katalogu z innym pakietem testowym.
  2. Nie martw się, że jesteś zielony podczas opracowywania funkcji pomocnika, po prostu ręcznie śledź, które testy naprawdę chcesz zdać.
  3. Skomentuj (GASP!) Lub usuń linie w mergesorttym połączeniu merge; następnie po mergepowrocie do pracy włóż je z powrotem.

Wszystko to wygląda dla mnie głupio (a może patrzę na to źle?). Czy ktoś zna preferowane podejście?

Ray Toal
źródło
2
Częścią TDD jest pomoc w stworzeniu projektu oprogramowania. Częścią tego procesu projektowania jest odkrywanie, co jest potrzebne do uzyskania pożądanego rezultatu. W przypadku mergesort, ponieważ jest to już bardzo dobrze zdefiniowany algorytm, ten proces wykrywania nie jest wymagany, a następnie staje się kwestią odwzorowania tego, co już wiesz, że jest projektem, na serię testów jednostkowych. Prawdopodobnie w teście najwyższego poziomu potwierdzono, że testowana metoda akceptuje nieposortowane zbiory i zwraca posortowane ...
Robert Harvey,
1
... Kolejne testy jednostkowe stopniowo zagłębiałyby się w rzeczywistą mechanikę a mergesort. Jeśli szukasz „właściwego” sposobu na zrobienie tego, nie ma innego, niż być dokładnym w kwestii mapowania mergesortalgorytmu na serię testów jednostkowych; tzn. powinny odzwierciedlać to, co mergesortfaktycznie robi.
Robert Harvey,
4
Projekt nie wyrasta z samych testów jednostkowych; jeśli spodziewasz się, że mergesortprojekt wyłoni się naturalnie z refaktora czerwono-zielonego, nie stanie się to, dopóki nie pokierujesz procesem w oparciu o twoją wiedzę mergesort.
Robert Harvey,
1
W TDD mergemusi być wynaleziony tylko na etapie „refaktoryzacji”. Jeśli zauważysz, że mergemetoda ta może zostać wprowadzona do zaliczenia testu mergesort, najpierw wykonaj testy bez mergemetody. Następnie refaktoryzuj swoją implementację, wprowadzając mergemetodę.
Fabio

Odpowiedzi:

13

Oto kilka alternatywnych sposobów sprawdzenia dostępnych opcji. Ale najpierw zasady TDD od wuja Boba z naciskiem przeze mnie:

  1. Nie wolno pisać żadnego kodu produkcyjnego, chyba że ma to negatywny wynik pozytywnego testu jednostkowego.
  2. Nie wolno pisać więcej testów jednostkowych niż jest to wystarczające do zaliczenia; awarie kompilacji to awarie.
  3. Nie wolno pisać więcej kodu produkcyjnego, niż jest to wystarczające do zaliczenia jednego z nieudanych testów jednostkowych.

Tak więc jednym ze sposobów na odczytanie reguły nr 3 jest to, że potrzebujesz mergefunkcji, aby przejść test, abyś mógł ją wdrożyć - ale tylko w najbardziej podstawowej formie.

Lub, alternatywnie, zaczynasz od napisania operacji scalania w linii, a następnie przekształcasz ją w funkcję po uruchomieniu testu.

Inną interpretacją jest to, że piszesz w trybie scalania, wiesz, że będziesz potrzebować mergeoperacji (tzn. Nie jest to YAGNI, co właśnie „wystarczająca” reguła próbuje ograniczyć). Dlatego powinieneś zacząć od testów dla scalenia, a dopiero potem przejść do testów dla ogólnego sortowania.

kdgregory
źródło
To są naprawdę dobre obserwacje. Wcześniej myślałem o wbudowaniu i faktorowaniu, ale ponieważ mergejest to zaskakująco niechlujne, rozróżniające wielkość liter (a także przydatne jako samodzielne), pomysł zrobienia tego jako osobnej funkcji miał więcej sensu. Jednak styl robienia tego inline w swojej podstawowej formie, a następnie faktoryzowania go na etapie niebieskiego kapelusza, wydaje się być właściwy i bardzo tego szukałem.
Ray Toal,
@RayToal - Właściwie skłaniam się ku podejściu polegającemu na pełnym przetestowaniu mergeoperacji przed wykonaniem sortowania (a także na osobnym przetestowaniu partitionoperacji). Wydaje mi się, że powstające korzyści płyną z powolnego dążenia do znanego celu. W przypadku połączenia nie sądzę, że celem jest sortowanie w ogóle (ponieważ wtedy skończysz na sortowaniu bąbelkowym). Znasz podstawowe operacje, więc pracujesz w kierunku tych operacji; ten rodzaj jest przeważnie przemyślany.
kdgregory,
1
Jest czwarta opcja. Przekaż mergefunkcję mergesorti kpij z jej zachowania. Następnie wróć i mergenajpierw wykonaj test. Delegaci są niesamowici ™.
RubberDuck,
@RubberDuck Wyśmiewanie integralnej części domeny podstawowej może prowadzić do pewnych problemów, a dokładniej po uruchomieniu programu, a funkcje wyśmiewane i scalania różnią się co najmniej szczegółami. Takie podejście należy pozostawić dla przypadków, w których pracujesz z zasobami zewnętrznymi, na przykład skąd pochodzi lista do sortowania.
cllamach