Oto zasady Roberta C. Martina dla TDD :
- Nie wolno pisać żadnego kodu produkcyjnego, chyba że ma to negatywny wynik pozytywnego testu jednostkowego.
- Nie wolno pisać więcej testów jednostkowych niż jest to wystarczające do zaliczenia; awarie kompilacji to awarie.
- Nie wolno pisać więcej kodu produkcyjnego, niż jest to wystarczające do zaliczenia jednego z nieudanych testów jednostkowych.
Kiedy piszę test, który wydaje się opłacalny, ale przechodzi bez zmiany kodu produkcyjnego:
- Czy to znaczy, że zrobiłem coś złego?
- Czy powinienem unikać pisania takich testów w przyszłości, jeśli można im pomóc?
- Czy powinienem tam zostawić test, czy go usunąć?
Uwaga: Ja próbuje zadać to pytanie tutaj: Mogę zacząć od testów jednostkowych przechodzącej? Ale do tej pory nie byłem w stanie wystarczająco dobrze sformułować pytania.
testing
unit-testing
tdd
Daniel Kaplan
źródło
źródło
Odpowiedzi:
Mówi, że nie można napisać kodu produkcyjnego, chyba że ma on przejść test jednostkowy zakończony niepowodzeniem, nie że nie można napisać testu, który przejdzie od samego początku. Reguła ma na celu powiedzenie „Jeśli musisz edytować kod produkcyjny, upewnij się, że najpierw napisałeś lub zmieniłeś test”.
Czasami piszemy testy, aby udowodnić teorię. Test kończy się pomyślnie, co obala naszą teorię. Następnie nie usuwamy testu. Możemy jednak (wiedząc, że mamy kontrolę źródłową) przerwać kod produkcyjny, aby upewnić się, że rozumiemy, dlaczego on przeszedł, gdy się tego nie spodziewaliśmy.
Jeśli okaże się, że jest to poprawny i poprawny test i nie powiela on istniejącego testu, zostaw go tam.
źródło
Oznacza to, że albo:
Ta ostatnia sytuacja jest bardziej powszechna, niż mogłoby się wydawać. Jako całkowicie podstępny i trywialny (ale wciąż ilustrujący) przykład, powiedzmy, że napisałeś następujący test jednostkowy (pseudokod, bo jestem leniwy):
Ponieważ wszystko, czego naprawdę potrzebujesz, to wynik 2 i 3 dodanych razem.
Twoja metoda wdrożenia to:
Ale powiedzmy, że muszę teraz dodać 4 i 6 razem:
Nie muszę przepisywać mojej metody, ponieważ obejmuje ona już drugi przypadek.
Powiedzmy teraz, że dowiedziałem się, że moja funkcja Add naprawdę musi zwrócić liczbę o pewnym pułapie, powiedzmy 100. Mogę napisać nową metodę, która to przetestuje:
I ten test się teraz nie powiedzie. Teraz muszę przepisać moją funkcję
aby przejść.
Zdrowy rozsądek nakazuje, że jeśli
przechodzi, celowo nie powoduje to niepowodzenia metody, aby można było przejść test zakończony niepowodzeniem, aby można było napisać nowy kod umożliwiający zaliczenie testu.
źródło
add(2,3)
przejść, dosłownie zwróciłbyś 5. Zakodowane. Następnie napisałbyś test, dlaadd(4,6)
którego zmusiłbyś cię do napisania kodu produkcyjnego, który sprawia, że przechodzi on, nie przerywającadd(2,3)
jednocześnie. Można by skończyć zreturn x + y
, ale nie ruszy się z nim. W teorii. Oczywiście Martin (a może to ktoś inny, nie pamiętam) lubi dawać takie przykłady edukacji, ale nie spodziewa się, że w ten sposób napiszesz tak trywialny kod.Twój test zdał, ale się nie mylisz. Myślę, że tak się stało, ponieważ kod produkcyjny od początku nie jest TDD.
Załóżmy, że kanoniczny (?) TDD. Nie ma kodu produkcyjnego, ale kilka przypadków testowych (to oczywiście zawsze kończy się niepowodzeniem). Dodajemy kod produkcyjny do przejścia. Następnie zatrzymaj się tutaj, aby dodać więcej przypadków testowych zakończonych niepowodzeniem. Ponownie dodaj kod produkcyjny do przekazania.
Innymi słowy, twój test może być rodzajem testu funkcjonalności, a nie prostym testem jednostki TDD. Są to zawsze cenne atuty dla jakości produktu.
Osobiście nie lubię takich totalitarnych, nieludzkich zasad; (
źródło
W rzeczywistości ten sam problem pojawił się na dojo zeszłej nocy.
Zrobiłem szybkie badania nad tym. Oto co wymyśliłem:
Zasadniczo nie jest to wyraźnie zabronione przez reguły TDD. Być może potrzebne są dodatkowe testy, aby udowodnić, że funkcja działa poprawnie w przypadku danych ogólnych. W tym przypadku praktyka TDD zostaje odłożona na chwilę. Zauważ, że wkrótce opuszczenie praktyki TDD niekoniecznie oznacza łamanie reguł TDD, o ile w międzyczasie nie zostanie dodany kod produkcyjny.
Dodatkowe testy mogą być napisane, o ile nie są zbędne. Dobrą praktyką byłoby wykonanie testów podziału klas równoważności. Oznacza to, że testowane są przypadki brzegowe i co najmniej jedna sprawa wewnętrzna dla każdej klasy równoważności.
Jednym z problemów, które mogą wystąpić przy takim podejściu, jest to, że jeśli testy przejdą od początku, nie można zagwarantować, że nie ma fałszywych wyników pozytywnych. Oznacza to, że mogą zdać testy, ponieważ testy nie zostaną poprawnie zaimplementowane, a nie dlatego, że kod produkcyjny działa poprawnie. Aby temu zapobiec, kod produkcyjny należy nieznacznie zmienić, aby przerwać test. Jeśli to spowoduje, że test się nie powiedzie, najprawdopodobniej test zostanie poprawnie zaimplementowany, a kod produkcyjny można zmienić z powrotem, aby test powiódł się ponownie.
Jeśli chcesz po prostu ćwiczyć ścisłe TDD, możesz nie pisać żadnych dodatkowych testów, które przejdą od początku. Z drugiej strony w środowisku programowania korporacyjnego należy zrezygnować z praktyki TDD, jeśli dodatkowe testy wydają się przydatne.
źródło
Test, który przechodzi bez modyfikacji kodu produkcyjnego, nie jest z natury zły i często jest konieczny do opisania dodatkowego wymagania lub przypadku granicznego. Tak długo, jak twój test „wydaje się opłacalny”, tak jak mówisz, że tak, zachowaj go.
Problemem jest pisanie pozytywnego testu jako zamiennika faktycznego zrozumienia przestrzeni problemów.
Możemy sobie wyobrazić dwie skrajności: jeden programista, który pisze dużą liczbę testów „na wszelki wypadek”, jeden łapie błąd; oraz drugi programista, który dokładnie analizuje przestrzeń problemów przed napisaniem minimalnej liczby testów. Powiedzmy, że oboje próbują zaimplementować funkcję wartości bezwzględnej.
Pierwszy programista pisze:
Drugi programista pisze:
Implementacja pierwszego programisty może spowodować:
Implementacja drugiego programisty może spowodować:
Wszystkie testy przeszły pomyślnie, ale pierwszy programista nie tylko napisał kilka redundantnych testów (niepotrzebnie spowalniając ich cykl rozwojowy), ale także nie przetestował przypadku granicznego (
abs(0)
).Jeśli zauważysz, że piszesz testy, które pomyślnie przejdziesz bez modyfikowania kodu produkcyjnego, zadaj sobie pytanie, czy testy naprawdę dodają wartości, czy też musisz poświęcić więcej czasu na zrozumienie przestrzeni problemów.
źródło
abs(n) = n*n
i zdał.abs(-2)
. Podobnie jak w przypadku wszystkiego, kluczem jest umiar.