Czy egzekwowanie polecenia wykonania testów jednostkowych jest złą praktyką?

84

Piszę testy dla projektu, który składa się z wielu submodułów. Każdy napisany przeze mnie przypadek testowy działa niezależnie od siebie i usuwam wszystkie dane między testami.

Mimo że testy przebiegają niezależnie, rozważam wykonanie polecenia wykonania, ponieważ niektóre przypadki wymagają więcej niż jednego submodułu. Na przykład podmoduł generuje dane, a inny uruchamia zapytania o dane. Jeśli podmoduł generujący dane zawiera błąd, test podmodułu zapytania również się nie powiedzie, nawet jeśli sam podmoduł działa poprawnie.

Nie mogę pracować z fałszywymi danymi, ponieważ główną funkcjonalność, którą testuję, jest połączenie ze zdalnym serwerem czarnej skrzynki, który pobiera dane tylko z pierwszego podmodułu.

Czy w takim przypadku można egzekwować polecenie wykonania testu, czy też jest to zła praktyka? Wydaje mi się, że w tym zestawie jest zapach, ale nie mogę znaleźć lepszego rozwiązania.

edycja: pytanie brzmi: Jak zbudować testy, w których jeden test jest konfiguracją innego testu? ponieważ „poprzedni” test nie jest instalacją, ale testuje kod, który wykonuje konfigurację.

Ali Rasim Kocal
źródło
123
Jeśli testujesz połączenie ze zdalnym serwerem, to z definicji nie są to testy jednostkowe.
Telastyn
9
Pierwsza odpowiedź pomieszała mnie tutaj, ponieważ w swoim tytule powiedziałeś „Czy to zła praktyka?” a w podsumowaniu pytania napisałeś „czy to w porządku?” Każdy, kto odpowie „tak” lub „nie”, pomyli jedno z nich!
Liath,
8
Wygląda na to, że tworzysz zestaw testów integracyjnych. Nawet w tym przypadku jeden test nie powinien polegać na innych testach.
Low Flying Pelican
17
Jeśli zamówienie ma znaczenie, prawdopodobnie robisz to źle.
Mark Rogers,

Odpowiedzi:

236

Nie mogę pracować z fałszywymi danymi, ponieważ główną funkcjonalność, którą testuję, jest połączenie ze zdalnym serwerem czarnej skrzynki, który pobiera dane tylko z pierwszego podmodułu.

To dla mnie kluczowa część. Możesz mówić o „testach jednostkowych” i „działających niezależnie od siebie”, ale wszystkie brzmią tak, jakby były zależne od tego zdalnego serwera i zależne od „pierwszego podmodułu”. Wszystko brzmi więc ściśle powiązane i zależy od stanu zewnętrznego. W związku z tym piszesz testy integracyjne. Prowadzenie tych testów w określonej kolejności jest dość normalne, ponieważ są one wysoce zależne od czynników zewnętrznych. Zamówiony test, z opcją wczesnego wyjścia z testu, jeśli coś pójdzie nie tak, jest całkowicie akceptowalny w testach integracyjnych.

Ale warto też rzucić okiem na strukturę aplikacji. Możliwość wykpienia pierwszego submodułu i zewnętrznego serwera potencjalnie pozwoliłby na napisanie prawdziwych testów jednostkowych dla wszystkich pozostałych podmodułów.

David Arno
źródło
14
Nie wspominając o tym, że niektóre testy muszą konkretnie sprawdzić, czy oczekiwane zachowanie występuje, gdy zdalny serwer jest niedostępny.
Alexander
2
A może zamierzasz pisać testy integracyjne, a zatem kpiąc z danych nie osiągniesz tego, co próbujesz osiągnąć za pomocą tych testów.
Guy Schalnat,
10
Problem jest najprawdopodobniej, że Junit ma w nazwie „jednostkę”.
Thorbjørn Ravn Andersen
7
@ ThorbjørnRavnAndersen Dokładnie. Ludzie naturalnie piszą testy integracyjne, a nie testy jednostkowe, ponieważ testy integracyjne są zarówno o wiele bardziej przydatne, jak i znacznie trudniejsze do napisania niż „rzeczywiste” testy jednostkowe. Ponieważ jednak popularne ramy testowania zostały nazwane na cześć koncepcji testów jednostkowych, termin ten został dokupiony i oznaczał „wszelkie testy automatyczne” we współczesnym języku.
Mason Wheeler,
1
@MasonWheeler Lub nawet wybrani przez kierowników nietechnicznych, aby oznaczać testy akceptacyjne.
TKK
32

Tak, to zła praktyka.

Zasadniczo test jednostkowy ma na celu przetestowanie pojedynczej jednostki kodu (np. Pojedynczej funkcji opartej na znanym stanie).

Jeśli chcesz przetestować ciąg zdarzeń, które mogą się zdarzyć na wolności, potrzebujesz innego stylu testu, na przykład testu integracji. Jest to tym bardziej prawdziwe, jeśli korzystasz z usług strony trzeciej.

Aby przeprowadzić jednostkowe testowanie takich rzeczy, musisz znaleźć sposób na wstrzyknięcie danych zastępczych, na przykład implementację interfejsu usługi danych, który odzwierciedla żądanie sieciowe, ale zwraca znane dane z lokalnego pliku danych zastępczych.

Paweł
źródło
8
Zgoda. Myślę, że to zamieszanie wynika z faktu, że wiele osób ma pojęcie, że testy integracyjne muszą być kompleksowe i używają „testu jednostkowego” w odniesieniu do każdego testu, który testuje tylko jedną warstwę .
autofag
8
@autophage: Zdecydowanie się z tym zgadzam. W rzeczywistości zgadzam się z tym tak bardzo, że regularnie wpadam w tę samą pułapkę, mimo że zgadzam się, że to pułapka 😂
Lekkość
16

Wymuszone polecenie wykonania ma sens tylko wtedy, gdy przerwie się również test po pierwszej awarii.

Przerwanie uruchomienia testowego przy pierwszej awarii oznacza, że ​​każde uruchomienie testowe może wykryć tylko jeden problem i nie może znaleźć nowych problemów, dopóki wszystkie poprzednie problemy nie zostaną naprawione. Jeśli pierwszy test, który zostanie uruchomiony, wykryje problem, który wymaga naprawienia przez miesiąc, wówczas w tym miesiącu nie zostaną wykonane żadne testy.

Jeśli nie przerwiesz uruchomienia testowego przy pierwszej awarii, wymuszone polecenie wykonania nic ci nie kupi, ponieważ każdy nieudany test musi zostać zbadany. Nawet jeśli tylko w celu potwierdzenia, że ​​test w podmodule zapytania nie powiódł się z powodu awarii, która została również zidentyfikowana w podmodule generującym dane.

Najlepszą radą, jaką mogę udzielić, jest napisanie testów w taki sposób, aby łatwo było stwierdzić, kiedy awaria zależności powoduje niepowodzenie testu.

Bart van Ingen Schenau
źródło
7

Zapach, o którym mówisz, polega na zastosowaniu niewłaściwego zestawu ograniczeń i reguł do twoich testów.

Testy jednostkowe często mylone są z „automatycznym testowaniem” lub „automatycznym testowaniem przez programistę”.

Testy jednostkowe muszą być małe, niezależne i szybkie.

Niektóre osoby błędnie czytają to jako „automatyczne testy napisane przez programistę muszą być małe, niezależne i szybkie” . Ale to po prostu oznacza, że ​​jeśli twoje testy nie są małe, niezależne i szybkie, nie są to testy jednostkowe, a zatem niektóre zasady dotyczące testów jednostkowych nie powinny, nie mogą lub nie mogą mieć zastosowania do twoich testów. Trywialny przykład: powinieneś uruchamiać testy jednostkowe po każdej kompilacji, czego nie wolno robić w przypadku testów automatycznych, które nie są szybkie.

Chociaż twoje testy nie będące testami jednostkowymi oznaczają, że możesz pominąć jedną regułę i możesz mieć pewną współzależność między testami, odkryłeś również, że istnieją inne reguły, których mogłeś przegapić i będziesz musiał ponownie wprowadzić - coś w zakresie innego pytania .

Piotr
źródło
6

Jak wspomniano powyżej, to, co przeprowadzasz, wydaje się być testem integracyjnym, jednak stwierdzasz, że:

Na przykład podmoduł generuje dane, a inny uruchamia zapytania o dane. Jeśli podmoduł generujący dane zawiera błąd, test podmodułu zapytania również się nie powiedzie, nawet jeśli sam podmoduł działa poprawnie.

To może być dobre miejsce na rozpoczęcie refaktoryzacji. Moduł, który uruchamia zapytania o dane, nie powinien zależeć od konkretnej implementacji pierwszego (generującego dane) modułu. Zamiast tego lepiej byłoby wstrzyknąć interfejs zawierający metody dostępu do tych danych, które można następnie wyśmiewać w celu przetestowania zapytań.

na przykład

Jeśli masz:

class Queries {

    int GetTheNumber() {
        var dataModule = new Submodule1();
        var data = dataModule.GetData();
        return ... run some query on data
    }
}

Zamiast tego wolą:

interface DataModule {
    Data GetData();
}


class Queries {

    IDataModule _dataModule;

    ctor(IDataModule dataModule) {
       _dataModule = dataModule;
    }

    int GetTheNumber() {
        var data = _dataModule.GetData();
        return ... run some query on data
    }
}

Usuwa to zależność od zapytań w źródle danych i umożliwia konfigurację łatwo powtarzalnych testów jednostkowych dla określonych scenariuszy.

Paddy
źródło
6

Inne odpowiedzi wspominają, że zamawianie testów jest złe (co jest prawdą przez większość czasu), ale istnieje jeden dobry powód wymuszania kolejności wykonywania testów: upewnienie się, że powolne testy (tj. Testy integracyjne) działają po szybszych testach (testy które nie polegają na innych zasobach zewnętrznych). Zapewnia to szybsze wykonywanie większej liczby testów, co może przyspieszyć sprzężenie zwrotne.

Mike Holler
źródło
2
Byłbym bardziej skłonny zbadać, dlaczego pewien test jednostkowy działa powoli, a nie egzekwować zamówienie. Testy jednostkowe powinny być szybkie.
Robbie Dee,
OP twierdzi, że nie może pracować z fałszywymi danymi dla niektórych z tych testów. Oznacza to pewnego rodzaju trafienie do bazy danych, spowalniając wszystkie testy (nawet niektóre prawdziwe testy jednostkowe, które powinny działać szybko naturalnie). Jeśli ma inne testy, które nie wymagają trafień do bazy danych, będą one działać o rząd wielkości szybciej niż cokolwiek wymagającego trafień na dysku lub w sieci.
Mike Holler,
2
Myślę, że oboje macie rację; Robbie ma rację, że testy jednostkowe powinny być małe i szybkie i odizolowane od zależności, więc kolejność nie powinna mieć znaczenia, a losowe porządkowanie często zachęca do lepszego projektowania poprzez egzekwowanie tej niezależności; a Mike ma rację, że najpierw szybsze testy są bardzo, bardzo dobre do testów integracyjnych . Podobnie jak w odpowiedziach powyżej, częścią problemu jest terminologia testów jednostkowych a integracyjnych.
WillC
@MikeHoller To nie są testy jednostkowe. Naprawdę nie powinno być pomyłki co do tego, jakie są testy jednostkowe .
Robbie Dee,
@RobbieDee Używałem tylko terminologii używanej przez OP. Rozumiem, że to nie są prawdziwe testy jednostkowe. Jeśli chcesz walczyć o terminologię, przywołaj ją z OP. (dlatego wyjaśniłem „prawdziwymi testami jednostkowymi” w moim wcześniejszym komentarzu ”)
Mike Holler,