Wiemy, że pisanie testów JUnit pokazuje jedną konkretną ścieżkę przez twój kod.
Jeden z moich współpracowników skomentował:
Ręczne pisanie testów jednostkowych jest dowodem na przykład .
Pochodził z Haskell, który ma narzędzia takie jak Quickcheck i umiejętność rozumowania zachowania programu za pomocą typów .
Jego implikacją było to, że istnieje wiele innych kombinacji danych wejściowych, które nie są sprawdzane przez tę metodę, dla których twój kod nie jest testowany.
Moje pytanie brzmi: czy ręczne pisanie testów jednostkowych jest dowodem na przykład?
unit-testing
junit
Sokole Oko
źródło
źródło
Odpowiedzi:
Jeśli losowo wybierasz dane wejściowe do testowania, to przypuszczam, że możliwe jest, że używasz logicznego błędnego dowodu na podstawie przykładu.
Ale dobre testy jednostkowe nigdy tego nie robią. Zamiast tego zajmują się zakresami i przypadkami krawędzi.
Na przykład, jeśli napiszesz testy jednostkowe dla funkcji wartości bezwzględnej, która przyjmuje liczbę całkowitą jako dane wejściowe, nie musisz testować każdej możliwej wartości danych wejściowych, aby udowodnić, że kod działa. Aby uzyskać kompleksowy test, potrzebujesz tylko pięciu wartości: -1, 0, 1 oraz wartości maksymalnej i minimalnej dla wejściowej liczby całkowitej.
Te pięć wartości testuje każdy możliwy zakres i przypadek krawędzi funkcji. Nie trzeba testować każdej innej możliwej wartości wejściowej (tj. Każdej liczby, którą może reprezentować typ całkowity), aby uzyskać wysoki poziom pewności, że funkcja działa dla wszystkich wartości wejściowych.
źródło
int foo(int x) { return 1234/(x - 100); }
. Zauważ również, że (w zależności od tego, co testujesz) może być konieczne upewnienie się, że nieprawidłowe („poza zakresem”) dane wejściowe zwracają prawidłowe wyniki (np. Że `` find_thing (rzecz) `poprawnie zwraca pewien rodzaj statusu„ nie znaleziono ” jeśli czegoś nie znaleziono).-Inf
,Inf
,NaN
,1e-100
,-1e-100
,-0
,2e200
... Wolałabym nie trzeba robić tych wszystkich ręcznie.Każde testowanie oprogramowania przypomina „Dowód przez przykład”, a nie tylko testowanie jednostkowe przy użyciu narzędzia takiego jak JUnit. I to nie jest nowa mądrość, jest cytat z Dijkstry z 1960 roku, który mówi w zasadzie to samo:
(wystarczy zamienić słowa „pokazuje” na „dowody”). Dotyczy to jednak również narzędzi generujących losowe dane testowe. Liczba możliwych danych wejściowych dla funkcji w świecie rzeczywistym jest zwykle większa o rząd wielkości niż liczba przypadków testowych, które można wytworzyć i zweryfikować w oparciu o oczekiwany wynik w epoce wszechświata, niezależnie od metody generowania tych przypadków, więc nawet jeśli do generowania dużej ilości danych testowych używa się narzędzia generatora, nie ma gwarancji, że nie pominie się jednego przypadku testowego, który mógł wykryć określony błąd.
Losowe testy mogą czasami ujawnić błąd, który został przeoczony przez ręcznie utworzone przypadki testowe. Ale ogólnie rzecz biorąc, bardziej efektywne jest staranne wykonanie testów do testowanej funkcji i upewnienie się, że uzyskano pełny kod i pokrycie gałęzi przy jak najmniejszej liczbie przypadków testowych. Czasami jest wykonalną strategią łączenie testów generowanych ręcznie i losowo. Ponadto przy stosowaniu testów losowych należy zadbać o to, aby wyniki były odtwarzalne.
Dlatego ręcznie tworzone testy nie są gorsze od losowo generowanych testów, często wręcz przeciwnie.
źródło
Ręczne pisanie testów to „dowód na przykład”. Ale to samo dotyczy QuickCheck i w ograniczonym zakresie typów systemów. Wszystko, co nie jest prostą formalną weryfikacją, będzie ograniczone w tym, co mówi ci o twoim kodzie. Zamiast tego musisz myśleć w kategoriach względnej wartości podejść.
Testy generatywne, takie jak QuickCheck, są naprawdę dobre do zamiatania szerokiej gamy danych wejściowych. Jest również o wiele lepszy w rozwiązywaniu przypadkowych przypadków niż w testach ręcznych: biblioteki testów generatywnych będą z tym bardziej doświadczone niż ty. Z drugiej strony mówią tylko o niezmiennikach, a nie o konkretnych wynikach. Tak więc, aby sprawdzić, czy Twój program uzyskuje prawidłowe wyniki, nadal potrzebujesz testów ręcznych, aby to sprawdzić
foo(bar) = baz
.źródło