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?
unit-testing
code-quality
Wayne Werner
źródło
źródło
Odpowiedzi:
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.
źródło
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(...)
.źródło
W twoim konkretnym przykładzie możesz uciec od „jednego” potwierdzenia, jeśli robisz coś takiego:
To właśnie robię, aby uniknąć poczucia winy z powodu posiadania wielu twierdzeń w jednym teście.
źródło
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
Assume
. Właśnie się tego dowiedziałem dzisiaj.Jeśli kod konfiguracji dla kodu
target1
różni się od kodu konfiguracji dlatarget2
, 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
target1
itarget2
są 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).źródło