Zapytano mnie o to, jak przeprowadzić pakiet 65 000 000 000 testów i zastanawiam się, czy to normalne, że projekt z tak dużą liczbą testów jest normalny.
Czy pracowałeś przy projektach o tej charakterystyce?
Zapytano mnie o to, jak przeprowadzić pakiet 65 000 000 000 testów i zastanawiam się, czy to normalne, że projekt z tak dużą liczbą testów jest normalny.
Czy pracowałeś przy projektach o tej charakterystyce?
Odpowiedzi:
Z 65 miliardami testów wydaje się, że jesteś proszony o przetestowanie wszystkich możliwych danych wejściowych. Nie jest to przydatne - zasadniczo sprawdzasz, czy procesor działa poprawnie, a nie czy kod jest poprawny.
Zamiast tego powinieneś testować klasy równoważności . Spowoduje to drastyczne zmniejszenie zakresu wejść testowych.
Zastanów się także, czy możesz podzielić swój system na mniejsze części. Każdy element będzie łatwiej przetestować w izolacji, a następnie możesz wykonać testy integracji, które połączą wszystkie elementy.
Jeśli nadal chcesz mieć pewność, że niektóre z tych kombinacji danych wejściowych działają, być może możesz spróbować przetestować fuzz . Otrzymasz niektóre korzyści z testowania wielu różnych danych wejściowych, ale bez uruchomienia wszystkich 65 miliardów z nich.
Jeśli jest to prawdziwy pakiet testowy, to nie chcesz być blisko pracy nad nim.
Całe zadanie testera polega na znalezieniu równowagi między testowaniem na tyle dokładnie, aby mieć pewność, że uzyskasz „właściwe” wyniki, a pisaniem wystarczającej liczby testów, aby można je było przeprowadzić w rozsądnym czasie.
Wiele testów można wydzielić na „klasy równoważności”, co oznacza, że zamiast uruchamiać 3 miliardy testów, uruchamiasz 1, co daje rozsądny poziom pewności, że wszystkie inne testy w tej klasie równoważności przebiegłyby pomyślnie, gdybyś zdecydował się zmarnować czas je uruchamiać.
Powinieneś powiedzieć każdemu, kto myśli o przeprowadzeniu 65 miliardów testów, że musi wykonać lepszą pracę, wyodrębniając testy do klas równoważności.
Jest bardziej niż prawdopodobne, że osiągnąłeś liczbę 65 miliardów testów, obliczając wszystkie możliwe kombinacje danych wejściowych do testowanego systemu lub obliczając cykliczną złożoność i zakładając, że test musi być napisany dla każdej z tych unikalnych ścieżek wykonania.
Nie tak pisane są prawdziwe testy, ponieważ jak wskazali inni plakaty i komentatorzy, siła techniczna wymagana do wykonania 65 miliardówtesty są oszałamiające. To byłoby jak napisanie testu, który ćwiczy metodę dodawania dwóch liczb całkowitych poprzez podłączenie każdej możliwej permutacji dwóch 32-bitowych wartości i sprawdzenie wyniku. To całkowite szaleństwo. Musisz narysować linię i zidentyfikować podzbiór wszystkich możliwych przypadków testowych, które między nimi zapewniłyby, że system będzie zachowywał się zgodnie z oczekiwaniami w całym zakresie danych wejściowych. Na przykład. testujesz dodając kilka „zwykłych” liczb, testujesz kilka scenariuszy z liczbą ujemną, testujesz ograniczenia techniczne, takie jak scenariusze przepełnienia, i testujesz wszelkie scenariusze, które powinny spowodować błąd. Jak wspomniano, te różne typy testów wykonują „klasy równoważności”; pozwalają pobrać reprezentatywną próbkę możliwych danych wejściowych wraz ze wszelkimi znanymi „wartościami odstającymi”,
Rozważ jedną z podstawowych katas kodu, generator liczb rzymskich. Zadaniem, które należy wykonać przy użyciu technik TDD w stylu „dojo”, jest napisanie funkcji, która może przyjąć dowolną liczbę od 1 do 3000 i wygenerować poprawną liczbę rzymską dla tej wartości liczbowej.
Nie rozwiązujesz tego problemu, pisząc 3000 testów jednostkowych, jeden po drugim, i przekazując je kolejno. To szaleństwo; ćwiczenie zwykle trwa od jednej do dwóch godzin i będziesz tam przez kilka dni testując każdą indywidualną wartość. Zamiast tego stajesz się mądry. Zaczynasz od najprostszego przypadku bazowego (1 == „I”), zaimplementuj go za pomocą strategii „najmniej kodu” ( return "I";
), a następnie sprawdź, jak kod będzie się zachowywał niepoprawnie w innym oczekiwanym scenariuszu (2 == ” II ”). Wypłukać i powtórzyć; bardziej niż prawdopodobne, zastąpiłeś swoją początkową implementację czymś, co powtarza znak „I” tak często, jak to konieczne (jak return new String('I',number);
). To oczywiście przejdzie test na III, więc nie przejmuj się; zamiast tego piszesz test dla 4 == „IV”, o którym wiesz, że bieżąca implementacja wygrała ”
Lub, w bardziej analitycznym stylu, badasz każdą decyzję warunkową podjętą przez kod (lub musi być) i piszesz test mający na celu wprowadzenie kodu dla każdego możliwego wyniku każdej decyzji. Jeśli masz 5 instrukcji if (każda z prawdziwą i fałszywą gałęzią), każda z nich jest w pełni niezależna od drugiej, kodujesz 10 testów, a nie 32. Każdy test będzie miał na celu stwierdzenie dwóch rzeczy dotyczących konkretnej możliwej decyzji; najpierw podjęta jest prawidłowa decyzja, a następnie, że kod został wprowadzony przy spełnieniu tego warunku. Państwo nie kodować test dla każdej możliwej permutacji niezależnych decyzji. Jeśli decyzje są zależne, musisz przetestować ich więcej w kombinacji, ale takich kombinacji jest mniej, ponieważ niektóre decyzje są podejmowane tylko wtedy, gdy inna decyzja przyniosła określony wynik.
Czy to „normalne” ?, nie. Gdzie „normalne” jest definiowane jako średnie lub typowe doświadczenie. Nie mogę powiedzieć, że kiedykolwiek musiałem pracować nad takim projektem, ale byłem przy projekcie, w którym jeden na kilka milionów bitów zostałby odwrócony. Testowanie tego było ... wyzwaniem.
Czy jest to potencjalnie wymagane? Zależy to od gwarancji i specyfiki projektu. Na początku jest to trochę niewiarygodne, ale twoje pytanie jest mało szczegółowe.
Jak zauważyli inni (MichaelT), czas na wykonanie tego zadania za pomocą testów seryjnych czyni to niepraktycznym. Zatem równoległość staje się twoim pierwszym rozważaniem. Ile systemów testowych możesz rzucić na ten problem i jakie masz wsparcie przy zestawianiu wyników tych wielu systemów?
Jakie masz gwarancje niezawodnej replikacji testowanego urządzenia lub algorytmu? Oprogramowanie jest dość niezawodne w replikacji, ale urządzenia sprzętowe (zwłaszcza pierwszej generacji) mogą mieć problemy z produkcją. Niepowodzenie fałszywego testu w takim przypadku może wskazywać na zły algorytm lub urządzenie nie zostało poprawnie złożone. Czy musisz rozróżniać te dwa przypadki?
Musisz także zastanowić się, jak sam zweryfikujesz systemy testowe. Zakładając uzasadniony powód tak wielu przypadków testowych, będziesz potrzebować dużo automatyzacji. Automatyzacja musi zostać sprawdzona, aby upewnić się, że nie generuje błędów w generowaniu przypadków testowych. Wyrywkowe kontrole pod kątem błędów byłyby rzeczywiście odpowiednikiem znalezienia igły w stogu siana.
Ten link arstechnica może, ale nie musi, rzucić nieco światła na twoje rozważania dotyczące testowania. Klastry GPU są powszechnie używane do hakowania haseł metodą brute-force. Ten cytowany w artykule może can cycle through as many as 350 billion guesses per second
, więc ten sposób przedstawia twoje testy 65B w perspektywie. Prawdopodobnie jest to inna dziedzina, ale pokazuje, jak podejście do zadania pod różnymi kątami może dać realne rozwiązanie.
Nie sądzę, aby utrzymywanie testów 6.5e + 10 było na pierwszym miejscu, więc ich uruchomienie może być dyskusyjne. Nawet największe projekty, takie jak Debian ze wszystkimi jego pakietami, mają łącznie tylko kilkaset milionów SLOC.
Ale jeśli i tak musisz przeprowadzić ogromną liczbę testów, istnieje kilka strategii.
Nie uruchamiaj ich wszystkich. Najprawdopodobniej nie każdy test zależy od każdej ścieżki kodu. Zdefiniuj zależności między podsystemami i ich testami oraz między pakietami testów, a będziesz mógł uruchamiać tylko testy jednostkowe istotne dla konkretnej zmiany, tylko testy integracyjne zależne od tych testów jednostkowych itp.
Uruchom je równolegle. Przy tak ogromnej podstawie kodu prawdopodobnie masz ogromną farmę kompilacji (w JetBrains, stosunkowo niewielka operacja, zwykliśmy mieć 40-50 agentów kompilacji działających tylko na farmie ciągłej kompilacji / integracji IDEA). Ponieważ testy jednostkowe są niezależne, a testy integracyjne mogą ponownie wykorzystywać już zbudowany kod, testy są stosunkowo łatwe do zrównoleglenia.
Przestań biec wcześnie. Jeśli wiesz, że dany zestaw testów zależy od jego rozsądnego funkcjonowania od poprawności innego zestawu testów, możesz przeciąć cały łańcuch, gdy zobaczysz, że jedno łącze nie działa.
Oświadczenie: Nie jestem profesjonalnym inżynierem testowania. Weź powyższe z odrobiną soli.
Chociaż pojawiło się kilka dobrych sugestii, jak próbować się przekraść przy pomocy mniejszej liczby testów, poważnie wątpię, czy twój system ma tylko 65 miliardów kombinacji wejściowych. To mniej niż 36 bitów wejściowych. Załóżmy, że skorzystałeś już ze wszystkich rad podanych powyżej.
Jeśli wykonanie każdego testu zajmie około milisekundy, a testy zostaną rozprowadzone tylko na 10 procesorów (jeden normalny komputer), test rozpocznie się nieco ponad 69 dni. To trochę czasu, ale nie całkowicie nierozsądnie. Rozłóż na 100 procesorów (tuzin zwykłych komputerów lub jeden rozsądny komputer na serwerze), a testy zakończą się w ciągu 7 dni. Możesz je uruchamiać co tydzień, aby sprawdzić regresje.