Próbuję ćwiczyć TDD, używając go do opracowania takiego prostego, jak Bit Vector. Zdarza mi się używać Swift, ale jest to pytanie zależne od języka.
My BitVector
to struct
przechowuje singiel UInt64
i przedstawia nad nim interfejs API, który pozwala traktować go jak kolekcję. Szczegóły nie mają większego znaczenia, ale jest to dość proste. Wysokie 57 bitów to bity pamięci, a dolne 6 bitów to bity „zliczające”, które informują, ile bitów pamięci faktycznie przechowuje zawartą wartość.
Do tej pory mam garść bardzo prostych możliwości:
- Inicjator, który konstruuje puste wektory bitowe
count
Nieruchomość typuInt
isEmpty
Nieruchomość typuBool
- Operator równości (
==
). Uwaga: jest to operator równości wartości podobny doObject.equals()
Javy, a nie referencyjny operator równości jak==
w Javie.
Wpadam na kilka cyklicznych zależności:
Test jednostkowy, który testuje mój inicjalizator, musi zweryfikować, czy nowo zbudowany
BitVector
. Można to zrobić na jeden z 3 sposobów:- Czek
bv.count == 0
- Czek
bv.isEmpty == true
- Sprawdź to
bv == knownEmptyBitVector
Metoda 1 polega na
count
, metoda 2 polega naisEmpty
( na czym sama polegacount
, więc nie ma sensu jej używać), metoda 3 polega na==
. W każdym razie nie mogę przetestować mojego inicjalizatora w izolacji.- Czek
Test
count
na coś musi działać na czymś, co nieuchronnie testuje moje inicjatoryWdrożenie
isEmpty
polega nacount
Wdrożenie
==
polega nacount
.
Udało mi się częściowo rozwiązać ten problem, wprowadzając prywatny interfejs API, który konstruuje BitVector
z istniejącego wzorca bitowego (jako a UInt64
). Pozwoliło mi to na zainicjowanie wartości bez testowania innych inicjatorów, dzięki czemu mogłem „uruchomić pasek” na swojej drodze.
Aby moje testy jednostkowe były naprawdę testami jednostkowymi, robię mnóstwo włamań, które znacznie komplikują mój kod prod i test.
Jak dokładnie radzisz sobie z tego rodzaju problemami?
źródło
BitVector
jest idealnie drobnym rozmiarem jednostki do testów jednostkowych i natychmiast rozwiązuje problemy, których publiczni członkowieBitVector
potrzebują się nawzajem w celu przeprowadzenia sensownych testów.Odpowiedzi:
Za bardzo martwisz się szczegółami implementacji.
To nie ma znaczenia, że w bieżącej realizacji ,
isEmpty
opiera się nacount
(lub cokolwiek innego może mieć relacje): wszystko powinno być dbanie o to interfejs publiczny. Na przykład możesz mieć trzy testy:count == 0
.isEmpty == true
Są to wszystkie ważne testy, które stają się szczególnie ważne, jeśli kiedykolwiek zdecydujesz się na refaktoryzację wewnętrznych elementów swojej klasy, tak aby
isEmpty
miała inną implementację, na której nie można polegaćcount
- dopóki wszystkie testy nadal się sprawdzają, wiesz, że nie regresowałeś byle co.Podobne rzeczy dotyczą innych punktów - pamiętaj o przetestowaniu interfejsu publicznego, a nie wewnętrznej implementacji. TDD może się tu przydać, ponieważ wtedy będziesz pisać testy, których potrzebujesz,
isEmpty
zanim napiszesz jakąkolwiek implementację dla niego.źródło
Dokonujesz przeglądu swojego myślenia na temat „testu jednostkowego”.
Obiekt zarządzający zmiennymi danymi w pamięci jest zasadniczo maszyną stanu. Tak więc każdy cenny przypadek użycia będzie co najmniej wywoływał metodę umieszczania informacji w obiekcie i wywoływał metodę odczytu kopii informacji z obiektu. W interesujących przypadkach będziesz również przywoływać dodatkowe metody zmieniające strukturę danych.
W praktyce często tak wygląda
lub
Terminologia „test jednostkowy” - cóż, ma długą historię niezbyt dobrą.
Kent napisał pierwszą wersję SUnit w 1994 roku , port do JUnit był w 1998 roku, pierwszy szkic książki TDD był na początku 2002 roku. Zamieszanie miało dużo czasu na rozprzestrzenienie się.
Kluczową ideą tych testów (dokładniej nazywanych „testami programistycznymi” lub „testami programistycznymi”) jest to, że testy są od siebie odizolowane. Testy nie współużytkują żadnych modyfikowalnych struktur danych, więc można je uruchamiać jednocześnie. Nie ma obaw, że testy muszą być przeprowadzane w określonej kolejności, aby poprawnie zmierzyć rozwiązanie.
Podstawowym przypadkiem użycia tych testów jest to, że są one uruchamiane przez programistę między edycjami własnego kodu źródłowego. Jeśli wykonujesz czerwony zielony protokół refaktora, nieoczekiwany CZERWONY zawsze oznacza błąd w ostatniej edycji; cofniesz tę zmianę, sprawdź, czy testy są ZIELONE, i spróbuj ponownie. Próba zainwestowania w projekt, w którym każdy możliwy błąd jest wykrywany tylko przez jeden test, nie ma dużej przewagi.
Oczywiście, jeśli scalenie wprowadza błąd, to stwierdzenie, że błąd nie jest już trywialny. Istnieją różne kroki, które można podjąć, aby zapewnić łatwą lokalizację usterek. Widzieć
źródło
Ogólnie (nawet jeśli nie używasz TDD) powinieneś starać się pisać testy w jak największym stopniu, udając, że nie wiesz, jak to jest realizowane.
Jeśli faktycznie robisz TDD, powinno już tak być. Twoje testy są wykonywalną specyfikacją programu.
Wygląd wykresu połączeń pod testami nie ma znaczenia, o ile same testy są rozsądne i dobrze utrzymane.
Myślę, że twoim problemem jest twoje zrozumienie TDD.
Moim zdaniem twoim problemem jest to, że „miksujesz” swoje osobowości TDD. Twoje „test”, „kod” i „refaktoryzator” działają całkowicie niezależnie od siebie, najlepiej. W szczególności twoje osoby kodujące i refaktoryzujące nie mają żadnych zobowiązań do testów poza tym, aby zapewnić / utrzymać ich ekologiczność.
Oczywiście, w zasadzie najlepiej byłoby, gdyby wszystkie testy były ortogonalne i niezależne od siebie. Ale to nie dotyczy twoich dwóch osób TDD i zdecydowanie nie jest to ścisły, a nawet niekoniecznie realistyczny, trudny wymóg twoich testów. Zasadniczo: Nie wyrzucaj zdrowego rozsądku na temat jakości kodu, aby spróbować spełnić wymóg, o którym nikt cię nie prosi.
źródło