Podczas tworzenia systemu takiego jak sztuczna inteligencja, który może bardzo szybko podążać wieloma różnymi ścieżkami lub w rzeczywistości dowolnym algorytmem, który ma kilka różnych danych wejściowych, możliwy zestaw wyników może zawierać dużą liczbę permutacji.
Jakie podejście należy zastosować do korzystania z TDD podczas tworzenia systemu, który generuje wiele, wiele różnych kombinacji wyników?
Odpowiedzi:
Bardziej praktyczne podejście do odpowiedzi pdr . TDD polega raczej na projektowaniu oprogramowania niż na testowaniu. Testy jednostkowe służą do weryfikacji pracy w miarę upływu czasu.
Tak więc na poziomie testu jednostkowego musisz zaprojektować jednostki, aby mogły być testowane w całkowicie deterministyczny sposób. Możesz to zrobić, usuwając wszystko, co sprawia, że jednostka nie jest deterministyczna (np. Generator liczb losowych), i usuwając to. Powiedzmy, że mamy naiwny przykład metody decydującej, czy ruch jest dobry, czy nie:
Ta metoda jest bardzo trudna do przetestowania, a jedyne, co naprawdę można zweryfikować w testach jednostkowych, to jej granice ... ale to wymaga wielu prób dotarcia do granic. Zamiast tego streśćmy losową część, tworząc interfejs i konkretną klasę, która otacza funkcjonalność:
Decider
Klasa musi teraz korzystać z betonu klasy poprzez abstrakcję, czyli interfejsu. Ten sposób robienia rzeczy nazywa się wstrzykiwaniem zależności (poniższy przykład jest przykładem wstrzykiwania konstruktora, ale można to również zrobić za pomocą settera):Możesz zadać sobie pytanie, dlaczego ten „nadęty kod” jest konieczny. Cóż, na początek, możesz teraz kpić z zachowania losowej części algorytmu, ponieważ
Decider
teraz ma zależność, która następuje poIRandom
„kontrakcie”. Możesz użyć do tego frameworku, ale ten przykład jest wystarczająco prosty do samodzielnego kodowania:Najlepsze jest to, że może to całkowicie zastąpić „faktyczne” konkretne wdrożenie. Kod można łatwo przetestować w następujący sposób:
Mam nadzieję, że daje to pomysły na zaprojektowanie aplikacji, dzięki czemu można wymusić permutacje, aby można było przetestować wszystkie przypadki brzegowe i tak dalej.
źródło
Ścisłe TDD często psuje się w przypadku bardziej złożonych systemów, ale nie ma to większego znaczenia w praktyce - kiedy wyjdziesz poza możliwość izolacji pojedynczych wejść, po prostu wybierz kilka przypadków testowych, które zapewniają rozsądny zasięg i wykorzystaj je.
Wymaga to pewnej wiedzy na temat tego, co wdrożenie będzie robić dobrze, ale jest to bardziej kwestia teoretyczna - jest mało prawdopodobne, aby budowanie sztucznej inteligencji zostało szczegółowo określone przez użytkowników nietechnicznych. Jest to ta sama kategoria, co przekazywanie testów na stałe do przypadków testowych - oficjalnie test jest specyfikacją, a implementacja jest zarówno poprawna, jak i najszybsze możliwe rozwiązanie, ale tak naprawdę nigdy się nie zdarza.
źródło
TDD nie polega na testowaniu, ale na projektowaniu.
Daleko od rozpadu złożoności, wyróżnia się w tych okolicznościach. Zaprowadzi Cię to do rozważenia większego problemu w mniejszych częściach, co doprowadzi do lepszego projektu.
Nie próbuj testować każdej permutacji twojego algorytmu. Po prostu buduj test po teście, napisz najprostszy kod, aby test działał, dopóki nie obejmiesz swoich podstaw. Powinieneś zobaczyć, co mam na myśli mówiąc o rozwiązaniu problemu, ponieważ będziesz zachęcany do udawania części problemu podczas testowania innych części, aby zaoszczędzić sobie konieczności pisania 10 miliardów testów dla 10 miliardów permutacji.
Edycja: Chciałem dodać przykład, ale nie miałem czasu wcześniej.
Rozważmy algorytm sortowania na miejscu. Możemy napisać testy, które obejmują górny koniec tablicy, dolny koniec tablicy i wszelkiego rodzaju dziwne kombinacje w środku. Dla każdego z nich musielibyśmy zbudować pełną tablicę jakiegoś rodzaju obiektu. To zajmie trochę czasu.
Lub możemy rozwiązać problem w czterech częściach:
Pierwszy jest jedyną skomplikowaną częścią problemu, ale poprzez oderwanie go od reszty sprawiłeś, że jest on o wiele, wiele prostszy.
Drugi jest prawie na pewno obsługiwany przez sam obiekt, przynajmniej opcjonalnie, w wielu strukturach o typie statycznym dostępny będzie interfejs pokazujący, czy ta funkcjonalność jest zaimplementowana. Więc nie musisz tego testować.
Trzeci jest niezwykle łatwy do przetestowania.
Czwarty obsługuje tylko dwa wskaźniki, prosi klasę ruchu o przesunięcie wskaźników, wzywa do porównania, a na podstawie wyniku tego porównania wzywa do zamiany przedmiotów. Jeśli podrobiłeś pierwsze trzy problemy, możesz to bardzo łatwo przetestować.
Jak udało nam się tutaj stworzyć lepszy projekt? Powiedzmy, że uprościłeś to i wdrożyłeś sortowanie bąbelkowe. Działa, ale kiedy idziesz do produkcji i musi obsłużyć milion obiektów, jest o wiele za wolny. Wszystko, co musisz zrobić, to napisać nową funkcję przejścia i zamienić ją. Nie musisz radzić sobie ze złożonością obsługi pozostałych trzech problemów.
Przekonasz się, że jest to różnica między testowaniem jednostkowym a TDD. Tester jednostkowy powie, że sprawiło to, że twoje testy stały się kruche, a jeśli przetestowałeś proste dane wejściowe i wyjściowe, nie będziesz musiał pisać więcej testów dotyczących nowej funkcjonalności. TDDer powie, że odpowiednio rozdzieliłem obawy, aby każda klasa, którą mam, zrobiła jedną rzecz i jedną rzecz dobrze.
źródło
Nie można przetestować każdej permutacji obliczeń z wieloma zmiennymi. Ale to nic nowego, zawsze tak było w przypadku każdego programu o złożoności zabawkowej. Celem testów jest sprawdzenie właściwości obliczeń. Na przykład sortowanie listy zawierającej 1000 liczb wymaga pewnego wysiłku, ale każde indywidualne rozwiązanie można bardzo łatwo zweryfikować. Teraz, mimo że jest ich 1000! możliwe (klasy) dane wejściowe dla tego programu i nie można ich wszystkich przetestować, wystarczy całkowicie wygenerować 1000 danych losowych i sprawdzić, czy dane wyjściowe są rzeczywiście posortowane. Dlaczego? Ponieważ prawie niemożliwe jest napisanie programu, który niezawodnie sortuje 1000 losowo wygenerowanych wektorów bez ogólnej poprawności (chyba że celowo zmusisz go do manipulowania pewnymi magicznymi danymi wejściowymi ...)
Teraz ogólnie rzeczy są nieco bardziej skomplikowane. Tam naprawdę nie było błędów, gdy program pocztowy nie wyda e-maili do użytkowników, jeśli mają one „F” w jego nazwę i dzień tygodnia jest piątek. Ale uważam, że to zmarnowany wysiłek, próbując przewidzieć taką dziwność. Zestaw testowy powinien zapewnić ci pewność, że system robi to, czego oczekujesz od oczekiwanych danych wejściowych. Jeśli w niektórych funky działa funky, zauważysz to wkrótce po wypróbowaniu pierwszej funky, a następnie możesz napisać test specjalnie na tę sprawę (który zwykle obejmuje również całą klasę podobnych przypadków).
źródło
Weź skrzynie krawędzi i trochę losowych danych wejściowych.
Aby wziąć przykład sortowania:
Jeśli to działa szybko, możesz być całkiem pewien, że będzie działać dla wszystkich danych wejściowych.
źródło