Czy jest jakaś wartość w pisaniu testu jednostkowego, który jest podzbiorem innego testu?

15

Aby dać nieco wymyślony przykład, powiedzmy, że chcę przetestować, czy funkcja zwraca dwie liczby i że pierwsza jest mniejsza niż druga:

def test_length():
    result = my_function()
    assert len(result) == 2

def test_order()
    a, b = my_function()
    assert a < b

Tutaj, jeśli test_lengthzawiedzie, to test_orderrównież zawiedzie. Czy najlepiej jest pisać test_length, czy pomijać?

EDYCJA: zauważ, że w tej sytuacji oba testy są w większości niezależne od siebie, każdy może być uruchomiony w izolacji lub może być uruchomiony w odwrotnej kolejności, to nie ma znaczenia. Więc żadne z tych wcześniejszych pytań

jest duplikatem powyższego.

Mihai
źródło
2
@ GlenH7 - to wydaje się inne pytanie. Drugi to „jeśli masz funkcje, w których Awywołania Bi wyniki zwracają ten sam wynik, powinieneś przetestować oba Ai B”. Chodzi raczej o nakładanie się testów , a nie o testowane funkcje. (Chociaż jest to mylące, ponieważ obecnie są nazywane).
Telastyn
1
Ściśle mówiąc, testy te nie nakładają się na siebie (nie zależą od sukcesu). Funkcja lambda: type('', (), {'__len__': lambda self: 2})()przejdzie pierwszy, ale nie drugi.
liori
1
@ GlenH7: FWIW, tak naprawdę nie zmieniłem pytania, po prostu starałem się, aby było bezpieczne dla komara ;-) (@gnat: bez obrazy, tylko żartuję!)
Doc Brown

Odpowiedzi:

28

Może być wartość, ale to trochę zapach. Albo twoje testy nie są dobrze izolowane (ponieważ test_ordernaprawdę testują dwie rzeczy) lub jesteś zbyt dogmatyczny w testowaniu (robienie dwóch testów testujących tę samą logiczną rzecz).

W tym przykładzie połączę oba testy razem. Tak, oznacza to, że masz wiele twierdzeń. Szkoda Nadal testujesz jedną rzecz - wynik funkcji. Czasami w prawdziwym świecie oznacza to wykonanie dwóch kontroli.

Telastyn
źródło
Głosowałem za odpowiedzią, choć brakuje w niej jednej drobnej kwestii. W Pythonie drugi test również się nie powiedzie, gdy my_functionnie zwróci dokładnie dwóch wartości, bez żadnego potwierdzenia - ponieważ przypisanie spowoduje wyjątek. Tak więc właściwie nie ma potrzeby używania wielu twierdzeń i dwóch kontroli, aby przetestować to samo.
Doc Brown
@DocBrown - założyłem tyle samo, ale nie znam komunikatów o błędach Pythona, aby wiedzieć, czy warto mieć błąd potwierdzenia zamiast losowego wyjątku / błędu z powodów informacyjnych.
Telastyn
Myślę, że w obecnej sytuacji komunikat o błędzie byłby prawdopodobnie wystarczająco informacyjny. Ale generalnie może tak nie być. Dlatego zgadzam się z tobą, że ogólne podejście do takiej sprawy powinno polegać na „scaleniu” przy użyciu dwóch stwierdzeń. Jeśli okaże się, że test można dodatkowo uprościć, pomijając jedno z twierdzeń, w porządku.
Doc Brown
5
@DocBrown Inną wartością podczas jawnego sprawdzania za pomocą twierdzenia jest to, że dla każdego badającego test staje się jasne, że jest to błąd testowanego kodu, a nie sam test. Kiedy moje testy napotykają nieoczekiwane wyjątki, zawsze muszę poświęcić trochę czasu na zastanowienie się, który z nich jest rzeczywiście błędny.
Chris Hayes
@ChrisHayes: Napisałem „nie ma potrzeby”, a nie „nie ma w tym żadnej wartości”.
Doc Brown
5

Twoje testy powinny być jednoznaczne. Nie jest do końca wywnioskowane, że jeśli długość_tekstu się nie powiedzie, test_order się nie powiedzie.

Nie jestem pewien, jak to wygląda w napisanym przez Ciebie języku Python, ale jeśli len(result)jest to 3, to pierwszy nie powiedzie się, ale drugi może przejść (a jeśli nie w Pythonie, to na pewno w językach takich jak JavaScript).

Tak jak powiedziałeś, chcesz sprawdzić, czy funkcja zwraca dwie liczby i czy są w porządku. To dwa testy.

Duch Madary
źródło
1
It's not completely inferred that if text_length fails test_order fails- w Pythonie daje wyjątek.
Doc Brown
Myślę, że pytanie dotyczy przypadku, w którym niepowodzenie test_lengthoznacza awarię test_order. Fakt, że podobna para testów napisanych w Javascripcie nie zachowuje się tak samo jak te dwa testy Pythona, jest w pewnym sensie nieistotna.
Dawood mówi, że przywróć Monikę
2
@DavidWallace: pamiętaj, pytanie brzmiało: „Czy najlepszą praktyką jest napisanie test_length, czy pominięcie go”? W Javascript potrzebujesz obu testów, aby upewnić się, że nie umknął Ci problem (gdy nie scalisz dwóch testów w jeden). W Pythonie możesz bezpiecznie pominąć pierwsze (co jest odrzucone w pierwszym zdaniu tej odpowiedzi, a zadeklarowane jako „Nie jestem pewien” w drugim). Szczerze mówiąc, dobra odpowiedź wygląda inaczej. Jednak nie zanegowałem tej odpowiedzi, ponieważ dobrą stroną jest wzmianka o JavaScript.
Doc Brown
4
@DavidWallace: ponieważ jesteśmy tutaj programistami.com, a nie na stackoverflow.com, IMHO udzielając odpowiedzi, która może być zastosowana w więcej niż jednym języku, jest lepsze niż odpowiedź tylko dla określonego języka. Ale odnosząc się do określonego języka, odpowiedź powinna być poprawna na temat języka, a nie mieszać niektórych właściwości.
Doc Brown
1
Rzecz w tym, że pytanie brzmi „czy warto napisać test, który jest podzbiorem innego testu”, a ta odpowiedź brzmi: „w niektórych językach żaden z twoich testów nie jest podzbiorem drugiego”, a następnie dochodzi do wniosku, że oba testy są dlatego konieczne. Czyta mi to tak, jakby ktoś powiedział: „Twój kod w ogóle nie kompiluje się w C ++, a poza tym w C ++ funkcja może zwrócić tylko jedną wartość, więc nie trzeba jej testować, zwraca dwie. Napisz tylko jeden test” ;-)
Steve Jessop,
2

Jedyną wartością test_lengthtutaj jest to, że jeśli wszystko przeminie, sama jej obecność wskazuje, że „długość” została przetestowana.

Tak więc naprawdę nie ma potrzeby posiadania obu tych testów. Zachowaj tylko, test_orderale rozważ zmianę nazwy test_length_and_order.

Nawiasem mówiąc, używam nazw, które zaczynają się testod nieco niezdarnego. Jestem zwolennikiem nazw testowych, które faktycznie opisują warunek, który twierdzisz.

Dawood mówi, że przywraca Monikę
źródło
1
testBrodawka jest sensowne ramy testowej Pythona. Co oczywiście nie musi zmieniać tego, czy jesteś jego fanem, ale zmienia wagę argumentu potrzebnego, aby ktoś przestał go używać ;-)
Steve Jessop
@ SteveJessop Tak, ciągle zapominam, że jest to potrzebne. Kiedy jakiś czas temu pisałem testy w języku Python, nabrałem zwyczaju rozpoczynania ich od wszystkich test_that_, jak test_that_return_values_are_in_orderi tak dalej. Być może przyszła wersja środowiska testowego jakoś obejdzie ten wymóg.
Dawood mówi, że przywróć Monikę
@DavidWallace: możesz zmienić prefiks, jeśli chcesz.
Lie Ryan,
1

Te testy pozostawiłbym osobne, jeśli tak właśnie ewoluowały. Tak, test_orderprawdopodobnie zawiedzie, gdy test_lengthtak się stanie, ale na pewno wiesz, dlaczego.

Zgadzam się również, że jeśli test_orderpojawił się pierwszy i znalazłeś testowalną awarię, to wynik był prawdopodobnie nie dwóch wartości, dodając tę ​​kontrolę jako asserti zmieniając nazwę testu, aby mieć test_length_and_ordersens.

Gdybyście musieli również sprawdzić, czy typy zwracanych wartości są liczbami całkowitymi, uwzględnilbym to w tym teście wyników „zbiorczych”.

Ale zauważ, że teraz masz baterię testów na wynik my_function(). Jeśli istnieje wiele kontekstów (lub, co bardziej prawdopodobne, wiele parametrów) do przetestowania, może to być teraz podprogram do testowania wszystkich wyników my_function().

Jednak kiedy piszę testy jednostkowe, zwykle testuję zbocze i złe przypadki oddzielnie od dobrego wejścia i normalnej wydajności (częściowo dlatego, że większość moich testów „jednostkowych” to często mini testy integracyjne) i często z wieloma twierdzeniami, które są tylko zepsute na osobne testy, jeśli zawiodą w sposób, w którym uważam, że chcę więcej informacji bez debugowania.

Prawdopodobnie zacznę od twoich oddzielnych testów, a następnie rozwinię test_lengthje test_length_and_typesi pozostawię test_orderosobne, zakładając, że to drugie jest postrzegane jako „normalne przetwarzanie”.

Mark Hurd
źródło