Czy przestrzeganie jednego twierdzenia na test jest głupią konsekwencją w tym przypadku?

10

Mam zajęcia, które testuję. Klasa ma funkcję:apply(List<IRule> rules, List<ITarget> targets);

W jednym teście chcę upewnić się, że każdy cel został przekazany do jednej reguły, a la:

rule1.AssertWasCalled(fnord => fnord.Test(target1));
rule1.AssertWasCalled(fnord => fnord.Test(target2));
rule1.AssertWasCalled(fnord => fnord.Test(target3));

Wydaje mi się, że ograniczenie się do jednego stwierdzenia byłoby dość hobgoblinem . Czy mam rację w tym założeniu, czy też jest jakiś inny sposób, w jaki mogę stwierdzić, że każdy cel został w rzeczywistości przetestowany?

Wayne Werner
źródło
Widzę fordów!
Ross Patterson

Odpowiedzi:

15

Te trzy twierdzenia są w istocie jednym testem. Testujesz zachowanie metody w kolekcji, aby upewnić się, że każdy element był parametrem dla określonego wywołania (tj. Że każdy element został poprawnie przetworzony).

Konfigurowanie danych trzykrotnie i na trzy różne metody jest marnotrawstwem i mniej czytelne niż alternatywa posiadania kilku twierdzeń.

„Reguła” pojedynczego stwierdzenia polega raczej na tworzeniu twierdzeń różnych typów za pomocą tych samych metod (zasadniczo testowanie różnych rzeczy), co tak naprawdę nie ma zastosowania w tym przypadku, w którym testujesz pod kątem pojedynczego zachowania.

Oded
źródło
3
Rzeczywiście: reguła jest więcej jednym logicznym potwierdzeniem na test jednostkowy. Możesz pogrupować je w jeden wyższy poziom, twierdząc, że możesz użyć ich ponownie w różnych testach.
Laurent Bourgault-Roy,
5

Uważam, że ten jeden argument na regułę testu istnieje, aby twoje testy koncentrowały się na jednym zagadnieniu. Jeśli przetestujesz 20 rzeczy w jednym teście, naprawdę trudno powiedzieć, jaki jest twój zasięg. Wiesz, że powoduje problem, gdy nie możesz nazwać metody testowej bez słowa i słowa . Na przykład, jeśli twoja metoda testowa byłaby dokładniej nazwana jako testFooIsTrueAndDbExistsAndBarIsNullAndAnExceptionDoesntOccur(), prawdopodobnie testujesz zbyt wiele w jednym teście.

W twoim przypadku myślę, że zapewne trzykrotnie jest zapewnione. Jeśli chcesz, aby kod był bardziej czytelny, możesz wyodrębnić te trzy twierdzenia do metody o nazwie assertWasCalledOnTargets(...).

Daniel Kaplan
źródło
3

W twoim konkretnym przykładzie możesz uciec od „jednego” potwierdzenia, jeśli robisz coś takiego:

foreach target in targets
{
     rule1.AssertWasCalled(fnord => fnord.Test(target))
}

To właśnie robię, aby uniknąć poczucia winy z powodu posiadania wielu twierdzeń w jednym teście.

Jeff B.
źródło
Zrobiłem to wcześniej. Niezła droga. Jest łatwy do odczytania i można zrozumieć naturę jego działania.
CokoBWare,
1

Walczyłem też z tym.

Purysta (we mnie) nalega na jedno stwierdzenie na test, więc będę wiedział * dokładnie *, gdzie wszystko wybuchło.

A potem odkrywam, że wycinam / wklejam dużo tego samego, zbędnego kodu konfiguracji testu. Po trzeciej lub czwartej warstwie zaczynasz mówić „O! Dosyć!”

Mój kompromis polegał na znalezieniu aspektów, które „nigdy” się nie psują. I poskładam te kawałki razem, a następnie dodam jeden nowy element, który mógłby się złamać. Dla jasności, nakładanie wielu niestabilnych obszarów w jednym teście stanowiłoby naruszenie tego kompromisu.


źródło
1
Powinieneś sprawdzić Assume. Właśnie się tego dowiedziałem dzisiaj.
Wayne Werner,
1

Jeśli kod konfiguracji dla kodu target1różni się od kodu konfiguracji dla target2, ten typ cięcia narożników może ostatecznie prowadzić do zbyt długiego kodu inicjalizacji testu. To z kolei jest bałaganem lub kończy się refaktoryzacja i ponowne użycie. Jeśli twoje testy są wystarczająco złożone, aby uzasadnić ich refaktoryzację, prawdopodobnie testujesz więcej niż jedną rzecz.

Jeśli kod konfiguracji dla każdego celu jest zasadniczo taki sam, podzielenie testu na wiele pojedynczych testów jest prawdopodobnie przesadą.

Jeśli target1i target2są różne implementacje tego samego interfejsu, powinieneś zamiast tego dodać test jednostkowy do interfejsu (i pozwolić twojemu środowisku testowemu wygenerować test dla każdej implementacji tego interfejsu).

Brian
źródło