Jednostka testująca wiele warunków w instrukcji IF

26

Mam fragment kodu, który wygląda mniej więcej tak:

function bool PassesBusinessRules()
{
    bool meetsBusinessRules = false;

    if (PassesBusinessRule1 
         && PassesBusinessRule2
         && PassesBusinessRule3)
    {
         meetsBusinessRules= true;
    }

    return meetsBusinessRules;
}

Uważam, że dla tej konkretnej funkcji powinny być cztery testy jednostkowe. Trzy, aby przetestować każdy z warunków w instrukcji if i upewnić się, że zwraca false. I kolejny test, który upewnia się, że funkcja zwraca true.

Pytanie: Czy zamiast tego powinno być dziesięć testów jednostkowych? Dziewięć, które sprawdza każdą z możliwych ścieżek awarii. TO ZNACZY:

  • Fałsz Fałsz Fałsz
  • Fałsz Fałsz Prawda
  • Fałsz Prawda Fałsz

I tak dalej dla każdej możliwej kombinacji.

Myślę, że to przesada, ale niektórzy inni członkowie mojego zespołu nie. Patrzę na to, jeśli BusinessRule1 zawiedzie, to zawsze powinien zwracać false, nie ma znaczenia, czy został sprawdzony pierwszy czy ostatni.

bwalk2895
źródło
Czy kompilator używa chciwej oceny dla operatora &&?
suszterpatt
12
Gdybyś napisał 10 testów jednostkowych, testowałbyś & operator, a nie swoje metody.
Mert Akcakaya,
2
Czy nie byłoby tylko ośmiu testów, gdyby przetestowano wszystkie możliwe kombinacje? Trzy parametry logiczne włączone lub wyłączone.
Kris Harper,
3
@Mert: Tylko jeśli możesz zagwarantować, że && zawsze tam będzie.
Misko
Hickey: Jeśli spędzimy to na pisaniu testów, wtedy nie spędzimy czasu na robieniu czegoś innego. Każdy z nas musi ocenić, jak najlepiej spędzić czas, aby zmaksymalizować wyniki, zarówno pod względem ilości, jak i jakości. Jeśli ludzie myślą, że spędzanie pięćdziesięciu procent czasu na pisaniu testów maksymalizuje ich wyniki - w porządku. Jestem pewien, że to nie jest prawda - wolałbym spędzać ten czas na myśleniu o moim problemie. Jestem pewien, że dla mnie daje to lepsze rozwiązania, z mniejszą liczbą wad, niż jakikolwiek inny sposób wykorzystania mojego czasu. Zły projekt z kompletnym zestawem testów jest nadal złym projektem.
Job

Odpowiedzi:

29

Formalnie te rodzaje pokrycia mają nazwy.

Po pierwsze, istnieje predykat : chcesz mieć przypadek testowy, który sprawi, że instrukcja if będzie prawdziwa, i taki, który sprawi, że będzie fałszywy. Spełnienie tego pokrycia jest prawdopodobnie podstawowym wymogiem dla dobrego zestawu testów.

Potem jest Pokrycie Warunków : Tutaj chcesz sprawdzić, czy każdy warunek podrzędny w if ma wartość true i false. To oczywiście tworzy więcej testów, ale zwykle wykrywa więcej błędów, więc często dobrym pomysłem jest dołączenie do zestawu testów, jeśli masz czas.

Najbardziej zaawansowane kryteria pokrycia nazywane są zwykle kombinatorycznym pokryciem warunków : tutaj celem jest stworzenie przypadku testowego, który przejdzie wszystkie możliwe kombinacje wartości logicznych w teście.

Czy jest to lepsze niż proste orzeczenie lub pokrycie warunków? Oczywiście pod względem zasięgu. Ale to nie jest darmowe. Jest to bardzo kosztowne w utrzymaniu testów. Z tego powodu większość ludzi nie zawraca sobie głowy pełnym zasięgiem kombinatorycznym. Zwykle testowanie wszystkich gałęzi (lub wszystkich warunków) będzie wystarczające do wyłapywania błędów. Dodanie dodatkowych testów do testowania kombinatorycznego zwykle nie wykrywa większej liczby błędów, ale wymaga dużo wysiłku, aby je stworzyć i utrzymać. Dodatkowy wysiłek zwykle sprawia, że ​​nie jest to warte bardzo małej wypłaty, więc nie poleciłbym tego.

Część tej decyzji powinna opierać się na ryzykownym podejściu do tego kodu. Jeśli ma dużo miejsca na awarię, warto ją przetestować. Jeśli jest nieco stabilny i niewiele się zmieni, powinieneś rozważyć skoncentrowanie wysiłków na testowaniu gdzie indziej.

Oleksi
źródło
2
Jeśli wartości boolowskie są przekazywane ze źródeł zewnętrznych (co oznacza, że ​​nie zawsze są one sprawdzane), często konieczne jest łączenie warunkowe. Najpierw utwórz tabelę kombinacji. Następnie dla każdego wpisu zdecyduj, czy ten wpis reprezentuje sensowny przypadek użycia. Jeśli nie, powinien być gdzieś kod (twierdzenia oprogramowania lub klauzula sprawdzająca poprawność), aby zapobiec wykonaniu tej kombinacji. Ważne jest, aby nie łączyć wszystkich parametrów w jednym teście kombinatorycznym: spróbuj podzielić parametry na grupy, które oddziałują ze sobą, tj. Mają to samo wyrażenie boolowskie.
rwong
Jak bardzo jesteś pewien śmiałych terminów? Twoja odpowiedź wydaje się być jedynym wystąpieniem „kombinatorycznego pokrycia warunków”, a niektóre zasoby mówią, że „orzeczenie pokrycia” i „ubezpieczenie warunkowe” są tym samym.
Stijn,
8

Ostatecznie zależy to od Ciebie (zespołu), kodu i konkretnego środowiska projektu. Nie ma uniwersalnej zasady. Zespół (r) powinien napisać tyle testów, ile potrzeba, aby upewnić się, że kod jest rzeczywiście poprawny . Więc jeśli twoi koledzy z drużyny nie przekonają 4 testów, być może potrzebujesz więcej.

Czas OTOH na napisanie testów jednostkowych jest zwykle rzadkim zasobem. Staraj się więc znaleźć najlepszy sposób na spędzenie ograniczonego czasu . Na przykład, jeśli masz inną ważną metodę z pokryciem 0%, może być lepiej napisać kilka testów jednostkowych, aby ją objąć, niż dodać dodatkowe testy dla tej metody. Oczywiście zależy to również od tego, jak delikatne jest wdrożenie każdego z nich. Planowanie wielu zmian tej konkretnej metody w dającej się przewidzieć przyszłości może uzasadniać dodatkowe pokrycie testem jednostkowym. Może być na krytycznej ścieżce w programie. Są to wszystkie czynniki, które tylko ty (zespół) możesz ocenić.

Osobiście zazwyczaj byłbym zadowolony z 4 testów, które nakreśliłeś, to znaczy:

  • prawda fałsz fałsz
  • fałsz prawda fałsz
  • fałsz fałszywy prawda
  • prawda prawda prawda

plus może jeden:

  • prawda prawda prawda fałsz

aby upewnić się, że jedynym sposobem na uzyskanie wartości zwrotu truejest spełnienie wszystkich 3 reguł biznesowych. Ale ostatecznie, jeśli twoi koledzy z drużyny nalegają na uwzględnienie ścieżek kombinatorycznych, dodanie tych dodatkowych testów może być tańsze niż kontynuowanie sporu o wiele dłużej :-)

Péter Török
źródło
3

Jeśli chcesz być bezpieczny, potrzebujesz ośmiu testów jednostkowych przy użyciu warunków reprezentowanych przez trzy zmienne tabele prawdy ( http://teach.valdosta.edu/plmoch/MATH4161/Spring%202004/and_or_if_files/image006.gif ).

Nigdy nie możesz być pewien, że logika biznesowa zawsze określa, że ​​kontrole są przeprowadzane w tej kolejności i chcesz, aby test wiedział jak najmniej o rzeczywistej implementacji.

smp7d
źródło
2
Testy jednostkowe to testy białych skrzynek.
Péter Török,
Cóż, porządek nie powinien mieć znaczenia, a && jest komunikatywny, a przynajmniej powinien być
Zachary K
2

Tak, powinna istnieć pełna kombinacja w idealnym świecie.

Podczas wykonywania testu jednostkowego naprawdę powinieneś spróbować zignorować sposób działania metody. Wystarczy podać 3 dane wejściowe i sprawdzić, czy dane wyjściowe są prawidłowe.

Telastyn
źródło
1
Testy jednostkowe to testy białych skrzynek. I nie żyjemy w idealnym świecie.
Péter Török,
@ PéterTörök - My nie żyjemy w idealnym świecie, aby mieć pewność, ale Stack Exchange Network nie zgadza się ze sobą na drugim miejscu. Szczególnie w przypadku TDD testy są zapisywane zgodnie ze specyfikacjami, a nie implementacją. Osobiście biorę „specyfikację”, aby uwzględnić wszystkie dane wejściowe (w tym zmienne składowe) i wszystkie dane wyjściowe (w tym skutki uboczne).
Telastyn
1
To tylko jeden konkretny wątek na StackOverflow, dotyczący konkretnego przypadku, którego nie należy nadmiernie generalizować. Zwłaszcza, że ​​ten post dotyczy oczywiście testowania kodu, który jest już napisany.
Péter Török,
1

Państwo jest złe. Poniższa funkcja nie wymaga testu jednostkowego, ponieważ nie ma skutków ubocznych i jest dobrze zrozumiane, co robi, a czego nie. Po co to testować? Nie ufasz własnemu mózgowi ??? Funkcje statyczne są świetne!

static function bool Foo(bool a, bool b, bool c)
{
    return a && b && c;
}
Praca
źródło
2
Nie, nie ufam własnemu mózgowi - nauczyłem się trudnego sposobu, aby zawsze dokładnie sprawdzać, co robię :-) Więc nadal potrzebowałbym testów jednostkowych, aby upewnić się, że np. Niczego nie wpisałem i że nikt nie idzie złamać kod w przyszłości. I więcej testów jednostkowych w celu weryfikacji metody wywołującej, która oblicza stan reprezentowany przez a, bi c. Możesz przenosić logikę biznesową w dowolny sposób, w końcu musisz gdzieś ją przetestować.
Péter Török
@ Péter Török, możesz również pisać literówki w swoich testach, a tym samym kończyć się fałszywymi pozytywami, więc gdzie się zatrzymać? Czy piszesz testy jednostkowe dla swoich testów jednostkowych? Nie ufam też mojemu mózgowi w 100%, ale pod koniec dnia pisanie kodu jest tym, co robię na życie. Możliwe jest, że w tej funkcji jest błąd, ale ważne jest, aby napisać kod w taki sposób, aby błąd można było łatwo prześledzić do źródła i aby po wyodrębnieniu problemu i dokonaniu naprawy lepiej było . Dobrze napisany kod może polegać na testach integracyjnych głównie infoq.com/presentations/Simple-Made-Easy
Job
2
Rzeczywiście testy też mogą być wadliwe. (TDD rozwiązuje ten problem, powodując, że testy kończą się niepowodzeniem.) Jednak dwukrotne popełnienie tego samego rodzaju błędu (i przeoczenie go) ma znacznie mniejsze prawdopodobieństwo. Ogólnie rzecz biorąc, żadna ilość i rodzaj testów nie może udowodnić, że oprogramowanie jest wolne od błędów , wystarczy zmniejszyć prawdopodobieństwo błędów do akceptowalnego poziomu. I w szybkości śledzenia błędów do źródła, IMO nic nie jest w stanie pokonać testów jednostkowych - szybkie sprzężenie zwrotne :-)
Péter Török
„Poniższa funkcja nie wymaga testu jednostkowego”. Myślę, że jesteś tutaj sarkastyczny, ale nie jest to jasne. Czy ufam własnemu mózgowi? NIE! Czy ufam mózgowi następnego faceta, który dotknie kodu? JESZCZE WIĘCEJ NIE! Czy ufam, że wszystkie założenia kodu będą prawdziwe za rok? ... rozumiesz? Również funkcje statyczne zabijają OO ... jeśli chcesz zrobić FP, to użyj języka FP.
Rob
1

Wiem, że to pytanie jest dość stare. Ale chcę nadać temu zagadnieniu inną perspektywę.

Po pierwsze, testy jednostkowe powinny mieć dwa cele:

  1. Twórz dokumentację dla siebie i swoich kolegów z zespołu, aby po upływie określonego czasu zapoznać się z testem jednostkowym i upewnić się, że rozumiesz what's the class' intentionihow the class is doing its work
  2. Podczas opracowywania test jednostkowy upewnia się, że kod, który piszemy, działa zgodnie z przeznaczeniem.

Podsumowując problem, chcemy przetestować complex if statement, dla podanego przykładu istnieją 2 ^ 3 możliwości, czyli ważną liczbę testów, które możemy napisać.

  • Możesz dostosować się do tego faktu i zapisać 8 testów lub skorzystać z testów sparametryzowanych
  • Możesz również postępować zgodnie z innymi odpowiedziami i pamiętać, że testy powinny być jasne z zamiarem, w ten sposób nie będziemy bałagać się zbyt wieloma szczegółami, które w najbliższej przyszłości mogą być trudniejsze do zrozumienia what is doing the code

Z drugiej strony, jeśli jesteś w stanie, że twoje testy są jeszcze bardziej złożone niż implementacja, to dlatego, że implementacja powinna zostać przeprojektowana (mniej więcej w zależności od przypadku) niż sam test.

Na przykład w przypadku złożonych instrukcji if możesz zastanowić się nad wzorcem odpowiedzialności łańcucha , implementując każdy moduł obsługi w ten sposób:

If some simple business rule apply, derive to the next handler

Jak łatwo byłoby przetestować różne proste reguły zamiast złożonej reguły?

Mam nadzieję, że to pomoże,

Cnexans
źródło
0

Jest to jeden z tych przypadków, w których coś takiego jak Quickcheck ( http://en.wikipedia.org/wiki/QuickCheck ) będzie twoim przyjacielem. Zamiast ręcznie zapisywać wszystkie N przypadków, komputer powinien wygenerować wszystkie (lub przynajmniej dużą liczbę) możliwych przypadków testowych i sprawdzić, czy wszystkie zwracają sensowny wynik.

Programujemy komputery do życia tutaj, dlaczego nie zaprogramować komputera do generowania dla ciebie przypadków testowych?

Zachary K.
źródło
0

Możesz zmienić warunki na warunki ochrony:

if (! PassesBusinessRule1) {
    return false;
}

if (! PassesBusinessRule2) {
    return false;
}

if (! PassesBusinessRule3) {
    return false;
}

Nie sądzę, żeby to zmniejszyło liczbę przypadków, ale z mojego doświadczenia wynika, że ​​łatwiej jest je rozdzielić w ten sposób.

(Zauważ, że jestem wielkim fanem „pojedynczego punktu wyjścia”, ale robię wyjątek dla warunków ochronnych. Są jednak inne sposoby strukturyzacji kodu, abyś nie miał osobnych zwrotów.)

Obrabować
źródło