Jak powinieneś TDD grać w Yahtzee?

36

Załóżmy, że piszesz grę TDD w stylu Yahtzee. Chcesz przetestować tę część kodu, która określa, czy zestaw pięciu rzutów matryc jest fularem, czy nie. O ile mi wiadomo, wykonując TDD, przestrzegasz następujących zasad:

  • Najpierw napisz testy
  • Napisz najprostszą możliwą rzecz, która działa
  • Udoskonal i refaktoryzuj

Tak więc początkowy test może wyglądać mniej więcej tak:

public void Returns_true_when_roll_is_full_house()
{
    FullHouseTester sut = new FullHouseTester();
    var actual = sut.IsFullHouse(1, 1, 1, 2, 2);

    Assert.IsTrue(actual);
}

Postępując zgodnie z „Napisz najprostszą możliwą rzeczą, która działa”, powinieneś teraz napisać następującą IsFullHousemetodę:

public bool IsFullHouse(int roll1, int roll2, int roll3, int roll4, int roll5)
{
    if (roll1 == 1 && roll2 == 1 && roll3 == 1 && roll4 == 2 && roll5 == 2)
    {
        return true;
    }

    return false;
}

Powoduje to zielony test, ale wdrożenie jest niekompletne.

Czy powinieneś przetestować każdą możliwą prawidłową kombinację (zarówno wartości, jak i pozycji) dla fula? Wygląda to na jedyny sposób, aby mieć absolutną pewność, że Twój IsFullHousekod został całkowicie przetestowany i poprawny, ale wydaje się to szalone.

Jak byś przetestował coś takiego?

Aktualizacja

Erik i Kilian podkreślają, że użycie literałów w początkowej implementacji w celu uzyskania zielonego testu może nie być najlepszym pomysłem. Chciałbym wyjaśnić, dlaczego to zrobiłem, a to wyjaśnienie nie pasuje do komentarza.

Moje praktyczne doświadczenie w testowaniu jednostkowym (szczególnie przy zastosowaniu metody TDD) jest bardzo ograniczone. Pamiętam, jak oglądałem nagranie TDD Masterclass Roy'a Osherove na Tekpub. W jednym z odcinków buduje styl TDD Kalkulatora Ciągów. Pełną specyfikację kalkulatora ciągów można znaleźć tutaj: http://osherove.com/tdd-kata-1/

Zaczyna od takiego testu:

public void Add_with_empty_string_should_return_zero()
{
    StringCalculator sut = new StringCalculator();
    int result = sut.Add("");

    Assert.AreEqual(0, result);
}

Powoduje to pierwszą implementację Addmetody:

public int Add(string input)
{
    return 0;
}

Następnie dodaje się ten test:

public void Add_with_one_number_string_should_return_number()
{
    StringCalculator sut = new StringCalculator();
    int result = sut.Add("1");

    Assert.AreEqual(1, result);
}

A Addmetoda jest refactored:

public int Add(string input)
{
    if (input.Length == 0)
    {
        return 0;
    }

    return 1;
}

Po każdym kroku Roy mówi „Napisz najprostszą rzecz, która zadziała”.

Pomyślałem więc, że spróbuję tego podejścia, próbując zrobić grę Yahtzee w stylu TDD.

Kristof Claes
źródło
8
„Napisz najprostszą możliwą rzecz, która działa” jest w rzeczywistości skrótem; poprawna rada brzmi: „Napisz najprostszą możliwą rzecz, która nie jest całkowicie braindead i oczywiście niepoprawna, która działa”. Więc nie, nie powinieneś pisaćif (roll1 == 1 && roll2 == 1 && roll3 == 1 && roll4 == 2 && roll5 == 2)
Carson63000,
3
Dziękujemy za podsumowanie odpowiedzi Erika, czy to w mniej kłótliwy, czy cywilizowany sposób.
Kristof Claes
1
„Napisz najprostszą rzecz, która działa”, na przykład @ Carson63000, jest w rzeczywistości uproszczeniem. Tak naprawdę tak myśleć; prowadzi do niesławnej klęski Sudoku TDD (google it). Kiedy ślepo przestrzegane, TDD jest rzeczywiście braindead: nie można uogólnić nie-trywialnego algorytmu, ślepo robiąc „najprostszą rzecz, która działa” ... trzeba naprawdę myśleć! Niestety, nawet rzekomi mistrzowie XP i TDD czasami podążają za nim na ślepo ...
Andres F.
1
@AndresF. Pamiętaj, że Twój komentarz pojawił się wyżej w wynikach wyszukiwania Google niż większość komentarzy dotyczących „awarii Soduko TDD” po mniej niż trzech dniach. Niemniej jednak, jak nie rozwiązać Sudoku, podsumowując: TDD jest za jakość, a nie poprawność. Musisz rozwiązać algorytm przed rozpoczęciem kodowania, szczególnie w TDD. (Nie to, że nie jestem też programistą pierwszego kodu.)
Mark Hurd
1
pvv.org/~oma/TDDinC_Yahtzee_27oct2011.pdf może być interesujący.

Odpowiedzi:

40

Istnieje już wiele dobrych odpowiedzi na to pytanie, a kilka z nich skomentowałem i poprawiłem. Mimo to chciałbym dodać kilka przemyśleń.

Elastyczność nie jest dla nowicjuszy

OP wyraźnie stwierdza, że nie ma doświadczenia z TDD i myślę, że dobra odpowiedź musi to wziąć pod uwagę. W terminologii modelu nabywania umiejętności Dreyfusa jest prawdopodobnie nowicjuszem . Nie ma nic złego w byciu nowicjuszem - wszyscy jesteśmy nowicjuszami, kiedy zaczynamy uczyć się czegoś nowego. Jednak model Dreyfusa wyjaśnia, że ​​charakteryzują się nowicjusze

  • sztywne przestrzeganie nauczanych zasad lub planów
  • brak wykonywania uznaniowego osądu

To nie jest opis niedoboru osobowości, więc nie ma się czego wstydzić - to etap, przez który wszyscy musimy przejść, aby nauczyć się czegoś nowego.

Dotyczy to również TDD.

Chociaż zgadzam się z wieloma innymi odpowiedziami tutaj, że TDD nie musi być dogmatyczne i że czasami praca w alternatywny sposób może być bardziej korzystna, ale to nie pomaga nikomu dopiero zaczynać. Jak możesz zastosować uznaniowy osąd, jeśli nie masz doświadczenia?

Jeśli nowicjusz zaakceptuje radę, że czasami nie jest dozwolone stosowanie TDD, jak może ustalić, kiedy można pominąć TDD?

Bez doświadczenia ani wskazówek jedyne, co może zrobić początkujący, to pominąć TDD za każdym razem, gdy staje się to zbyt trudne. To ludzka natura, ale nie jest to dobry sposób na naukę.

Posłuchaj testów

Pomijanie TDD w dowolnym momencie staje się utratą jednej z najważniejszych zalet TDD. Testy dostarczają wczesnych informacji zwrotnych na temat interfejsu API SUT. Jeśli test jest trudny do napisania, jest to ważny znak, że SUT jest trudny w użyciu.

Oto jeden z najważniejszych komunikatów GOOS : słuchaj swoich testów!

W przypadku tego pytania moją pierwszą reakcją, kiedy zobaczyłem proponowane API gry Yahtzee, i dyskusję na temat kombinatoryki, którą można znaleźć na tej stronie, było to, że jest to ważna opinia na temat API.

Czy API musi reprezentować rzuty kostką jako uporządkowaną sekwencję liczb całkowitych? Dla mnie ten zapach Prymitywnej Obsesji . Dlatego cieszyłem się, widząc odpowiedź z wysokości sugerującą wprowadzenie Rollklasy. Myślę, że to doskonała sugestia.

Myślę jednak, że niektóre komentarze do tej odpowiedzi są błędne. To, co sugeruje TDD, to to, że kiedy Rollwpadniesz na pomysł, że klasa byłaby dobrym pomysłem, zawiesisz pracę nad oryginalnym SUT i zaczniesz pracę nad TDD w Rollklasie.

Chociaż zgadzam się, że TDD jest bardziej ukierunkowane na „szczęśliwą ścieżkę” niż na kompleksowe testy, nadal pomaga rozbić system na możliwe do zarządzania jednostki. A Rolldźwięki klasy jak coś można znacznie łatwiej TDD do końca.

Następnie, gdy Rollklasa będzie wystarczająco rozwinięta, wróć do pierwotnego SUT i dopracuj go pod względem Rollnakładów.

Sugestia Pomocnika Testowego niekoniecznie oznacza losowość - to tylko sposób na uczynienie testu bardziej czytelnym.

Innym sposobem podejścia i modelowania danych wejściowych pod względem Rollinstancji byłoby wprowadzenie Konstruktora danych testowych .

Czerwony / zielony / refaktor to proces trzystopniowy

Chociaż zgadzam się z ogólnym sentymentem, że (jeśli masz wystarczające doświadczenie w TDD), nie musisz rygorystycznie trzymać się TDD, myślę, że to dość kiepska rada w przypadku ćwiczenia Yahtzee. Chociaż nie znam szczegółów reguł Yahtzee, nie widzę tutaj przekonującego argumentu, że nie można rygorystycznie trzymać się procesu Red / Green / Refactor i nadal osiągnąć właściwy wynik.

Większość ludzi tutaj zapomina o trzecim etapie procesu Red / Green / Refactor. Najpierw napisz test. Następnie piszesz najprostszą implementację, która przejdzie wszystkie testy. Potem refaktoryzujesz.

To tutaj, w tym trzecim stanie, możesz wnieść wszystkie swoje umiejętności zawodowe. Tutaj możesz zastanowić się nad kodem.

Jednak myślę, że jest to kłamstwo stwierdzające, że powinieneś tylko „Napisz najprostszą możliwą rzecz, która nie jest całkowicie braindead i oczywiście niepoprawna, która działa”. Jeśli wiesz (wcześniej), że wiesz wystarczająco dużo o implementacji, wtedy wszystko kompletne rozwiązanie będzie oczywiście niepoprawne . Jeśli chodzi o porady, jest to całkiem bezużyteczne dla początkującego.

To, co naprawdę powinno się zdarzyć, to to, że jeśli zdołasz zaliczyć wszystkie testy z oczywiście nieprawidłową implementacją, to powinieneś napisać kolejny test .

Zaskakujące jest to, jak często robienie tego prowadzi do zupełnie innej implementacji niż ta, o której myślałeś wcześniej. Czasami alternatywa, która rośnie w ten sposób, może okazać się lepsza niż twój pierwotny plan.

Rigor to narzędzie do nauki

Sensowne jest trzymanie się rygorystycznych procesów, takich jak czerwony / zielony / refaktor, o ile tylko się uczysz. Zmusza ucznia do zdobywania doświadczenia z TDD nie tylko wtedy, gdy jest to łatwe, ale także gdy jest trudne.

Tylko po opanowaniu wszystkich trudnych części jesteś w stanie podjąć świadomą decyzję, kiedy zejść z „prawdziwej” ścieżki. Wtedy zaczniesz tworzyć własną ścieżkę.

Mark Seemann
źródło
„tu są nowicjusze TDD, ze wszystkimi typowymi obawami związanymi z próbowaniem tego. Ciekawe podejście, jeśli zdołasz zaliczyć wszystkie testy z oczywiście nieprawidłową implementacją, to informacja zwrotna, że ​​powinieneś napisać kolejny test. Wydaje się, że to dobry sposób, aby zmierzyć się z przekonaniem, że testowanie implementacji „braindead” jest niepotrzebne.
shambulator
1
Wow dziękuję. Naprawdę boję się tendencji ludzi do mówienia początkującym w TDD (lub innej dyscyplinie), aby „nie martwili się zasadami, po prostu rób to, co najlepiej”. Skąd możesz wiedzieć, co najlepiej się czuje, gdy nie masz wiedzy ani doświadczenia? Chciałbym również wspomnieć o zasadzie priorytetu transformacji, albo kod ten powinien stać się bardziej ogólny, gdy testy stają się bardziej szczegółowe. najbardziej zagorzali zwolennicy TDD, tacy jak wujek bob, nie poparliby pojęcia „po prostu dodaj nową instrukcję if dla każdego testu”.
sara
41

Jako zrzeczenie się, jest to TDD, gdy go ćwiczę i, jak trafnie zauważył Kilian, uważałbym na każdego, kto zasugerowałby, że istnieje jeden właściwy sposób na jego przećwiczenie. Ale może ci to pomoże ...

Po pierwsze, najprostszą rzeczą, którą możesz zrobić, aby zdać test, jest:

public bool IsFullHouse(int roll1, int roll2, int roll3, int roll4, int roll5)
{
    return true;
}

Jest to istotne, ponieważ nie z powodu niektórych praktyk TDD, ale dlatego, że twarde pisanie we wszystkich tych literałach nie jest tak naprawdę dobrym pomysłem. Jedną z najtrudniejszych rzeczy do obejrzenia za pomocą TDD jest to, że nie jest to kompleksowa strategia testowania - to sposób na ochronę przed regresjami i oznaczanie postępu przy jednoczesnym utrzymaniu prostego kodu. To strategia rozwoju , a nie strategia testowania.

Powodem, dla którego wspominam o tym rozróżnieniu, jest to, że pomaga w określeniu, jakie testy powinieneś napisać. Odpowiedź na „jakie testy mam napisać?” to „wszelkie testy potrzebne do uzyskania kodu tak, jak chcesz”. Pomyśl o TDD jako sposobie, w jaki możesz pomóc dokuczać algorytmom i uzasadniać swój kod. Więc biorąc pod uwagę twój test i moją „prostą zieloną” implementację, jaki test będzie następny? Cóż, stworzyłeś coś, co jest domem pełnym, więc kiedy to nie jest dom pełen?

public void Returns_true_when_roll_is_full_house()
{
    FullHouseTester sut = new FullHouseTester();
    var actual = sut.IsFullHouse(1, 2, 3, 4, 5);

    Assert.IsFalse(actual);
}

Teraz musisz znaleźć sposób na rozróżnienie dwóch przypadków testowych, które mają znaczenie . Osobiście przydałbym się trochę wyjaśnień na temat „zrób najprostszą rzecz, aby zdać test” i powiem „zrób najprostszą rzecz, aby zdać test, który przyspieszy twoją implementację”. Pisanie testów zakończonych niepowodzeniem jest twoim pretekstem do zmiany kodu, więc kiedy zaczynasz pisać każdy test, powinieneś zadać sobie pytanie: „czego mój kod nie robi tak, jak chcę, i jak mogę go ujawnić?” Może to również pomóc w zwiększeniu odporności kodu i obsłudze przypadków brzegowych. Co robisz, jeśli dzwoniący wprowadza bzdury?

public void Returns_true_when_roll_is_full_house()
{
    FullHouseTester sut = new FullHouseTester();
    var actual = sut.IsFullHouse(-1, -2, -3, -4, -5);

    //I dunno - throw exception, return false, etc, whatever you think it should do....
}

Podsumowując, jeśli testujesz każdą kombinację wartości, prawie na pewno robisz to źle (i prawdopodobnie skończy się kombinatoryczną eksplozją warunkowych). Jeśli chodzi o TDD, powinieneś napisać minimalną liczbę przypadków testowych niezbędnych do uzyskania pożądanego algorytmu. Wszelkie dalsze testy, które napiszesz, zaczną być zielone, a tym samym staną się dokumentacją, a nie ściśle częścią procesu TDD. Kolejne przypadki testowe TDD napiszesz tylko wtedy, gdy wymagania się zmienią lub błąd zostanie ujawniony, w którym to przypadku udokumentujesz niedobór testem, a następnie go przejdziesz.

Aktualizacja:

Zacząłem to jako komentarz w odpowiedzi na twoją aktualizację, ale zaczęło być dość długo ...

Powiedziałbym, że problem nie polega na istnieniu literałów, kropki, ale na tym, że „najprostsze” jest 5-częściowe warunkowe. Kiedy się nad tym zastanowić, 5-częściowy warunek jest w rzeczywistości dość skomplikowany. Powszechne będzie stosowanie literałów podczas kroku od czerwonego do zielonego, a następnie ich abstrakcja do stałych na etapie refaktoryzacji lub uogólnianie ich w późniejszym teście.

Podczas mojej własnej podróży z TDD zdałem sobie sprawę, że należy dokonać istotnego rozróżnienia - nie jest dobrze pomylić „proste” i „tępe”. To znaczy, kiedy zaczynałem, patrzyłem, jak ludzie robią TDD, i myślałem, że „robią najgłupsze możliwe testy, aby testy przeszły pomyślnie” i przez pewien czas naśladowałem to, dopóki nie zdałem sobie sprawy, że „proste” było nieco inne niż „tępy”. Czasami pokrywają się, ale często nie.

Przepraszam, gdybym sprawiał wrażenie, że problemem jest istnienie literałów - nie jest. Powiedziałbym, że problemem jest złożoność warunku z 5 klauzulami. Twój pierwszy kolor czerwony do zielonego może być po prostu „prawdziwy”, ponieważ jest to naprawdę proste (i tępe, przypadkowo). Następny przypadek testowy z (1, 2, 3, 4, 5) będzie musiał zwrócić false, i wtedy zaczynasz pozostawiać „rozwarty”. Musisz zadać sobie pytanie „dlaczego (1, 1, 1, 2, 2) to fula, a (1, 2, 3, 4, 5) nie jest?” Najprostszą rzeczą, jaką możesz wymyślić, może być to, że jeden ma ostatni element sekwencji 5 lub drugi element sekwencji 2, a drugi nie. Są proste, ale są też (niepotrzebnie) tępe. To, na czym tak naprawdę chcesz jechać, to „ile mają takich samych numerów?” Możesz więc zdać drugi test, sprawdzając, czy jest powtórzenie. W jednym z powtórzeniami masz fula, aw drugim nie. Teraz test kończy się pomyślnie, a ty piszesz kolejny przypadek testowy, który ma powtórzenie, ale nie jest pełny, aby dopracować algorytm.

Możesz robić to z literałami lub nie, a jeśli tak, to w porządku. Ale ogólną ideą jest rozwijanie algorytmu „organicznie” w miarę dodawania kolejnych przypadków.

Erik Dietrich
źródło
Zaktualizowałem moje pytanie, aby dodać więcej informacji na temat tego, dlaczego zacząłem od dosłownego podejścia.
Kristof Claes
9
To świetna odpowiedź.
tallseth
1
Dziękuję bardzo za twoją przemyślaną i dobrze wyjaśnioną odpowiedź. To naprawdę ma sens teraz, kiedy o tym myślę.
Kristof Claes
1
Dokładne testowanie nie oznacza testowania każdej kombinacji ... To głupie. W tym konkretnym przypadku weź konkretny pełny dom lub dwa i kilka niepełnych domów. Również wszelkie specjalne kombinacje, które mogą powodować problemy (np. 5 w swoim rodzaju).
Schleis,
3
+1 Zasady tej odpowiedzi zostały opisane przez Robert C. Martin's Prioration
Mark Seemann
5

Testowanie pięciu konkretnych wartości literalnych w określonej kombinacji nie jest „najprostsze” dla mojego gorączkowego mózgu. Jeśli rozwiązanie problemu jest naprawdę oczywiste (policz, czy masz dokładnie trzy i dokładnie dwie dowolne wartości), to napewno napisz kod tego rozwiązania i napisz kilka testów, których bardzo, bardzo mało prawdopodobne jest przypadkowe spełnienie ilość napisanego kodu (tj. różne literały i różne rzędy potrójnych i podwójnych).

Maksymy TDD naprawdę są narzędziami, a nie przekonaniami religijnymi. Chodzi o to, aby szybko napisać poprawny, dobrze przemyślany kod. Jeśli maksyma oczywiście stoi na przeszkodzie temu, po prostu przeskocz do przodu i przejdź do następnego kroku. W twoim projekcie będzie wiele nieoczywistych bitów, w których możesz go zastosować.

Kilian Foth
źródło
5

Odpowiedź Erika jest świetna, ale pomyślałem, że mogę podzielić się pewną sztuczką podczas pisania testu.

Zacznij od tego testu:

[Test]
public void FullHouseReturnsTrue()
{
    var pairNum = AnyDiceValue();
    var trioNum = AnyDiceValue();

    Assert.That(sut.IsFullHouse(trioNum, pairNum, trioNum, pairNum, trioNum));
}

Ten test staje się jeszcze lepszy, jeśli utworzysz Rollklasę zamiast zaliczać 5 parametrów:

[Test]
public void FullHouseReturnsTrue()
{
    var roll = AnyFullHouse();

    Assert.That(sut.IsFullHouse(roll));
}

To daje tę implementację:

public bool IsFullHouse(Roll toCheck)
{
    return true;
}

Następnie napisz ten test:

[Test]
public void StraightReturnsFalse()
{
    var roll = AnyStraight();

    Assert.That(sut.IsFullHouse(roll), Is.False);
}

Gdy to minie, napisz to:

[Test]
public void ThreeOfAKindReturnsFalse()
{
    var roll = AnyStraight();

    Assert.That(sut.IsFullHouse(roll), Is.False);
}

Potem założę się, że nie musisz już pisać (może dwie pary, a może yahtzee, jeśli uważasz, że to nie jest fula).

Oczywiście, zastosuj dowolne metody, aby zwrócić losowe Rzuty spełniające twoje kryteria.

Podejście to ma kilka zalet:

  • Nie musisz pisać testu, którego jedynym celem jest powstrzymanie cię od utknięcia na określonych wartościach
  • Testy naprawdę ładnie przekazują twoją intencję (kod pierwszego testu krzyczy „każdy zwrot z domu jest prawdziwy”)
  • szybko dotrzesz do sedna problemu
  • czasami zauważy przypadki, o których nie pomyślałeś
wzrost
źródło
Jeśli zastosujesz to podejście, będziesz musiał poprawić swoje komunikaty dziennika w swoich instrukcjach Assert.This. Deweloper musi sprawdzić, które dane wejściowe spowodowały awarię.
Bringer128
Czy to nie stwarza dylematu „kurczak czy jajko”? Czy po wdrożeniu AnyFullHouse (również przy użyciu TDD) nie potrzebujesz IsFullHouse do weryfikacji jego poprawności? W szczególności, jeśli AnyFullHouse ma błąd, błąd ten może zostać zreplikowany w IsFullHouse.
Jemiołuszka
AnyFullHouse () to metoda w przypadku testowym. Czy zazwyczaj TDD testujesz przypadki? Nie. Ponadto o wiele łatwiej jest stworzyć losowy przykład fula (lub dowolnego innego rzutu) niż testowanie jego istnienia. Oczywiście, jeśli twój test zawiera błąd, można go zreplikować w kodzie produkcyjnym. Dotyczy to każdego testu.
tallseth
AnyFullHouse jest metodą „pomocniczą” w przypadku testowym. Jeśli są wystarczająco ogólne, przetestuj również metody pomocnicze!
Mark Hurd
Gdyby IsFullHouserzeczywiście powrócić truejeśli pairNum == trioNum ?
recursion.ninja
2

Mogę wymyślić dwa główne sposoby, które wziąłbym pod uwagę podczas testowania tego;

  1. Dodaj „kilka” więcej przypadków testowych (~ 5) prawidłowych zestawów full-house i taką samą liczbę oczekiwanych fal ({1, 1, 2, 3, 3}) jest dobrym. Pamiętaj, że na przykład 5 może być rozpoznane jako „3 takie same plus para” przez niepoprawną implementację). Ta metoda zakłada, że ​​programista nie tylko próbuje zdać testy, ale faktycznie zaimplementuje go poprawnie.

  2. Przetestuj wszystkie możliwe zestawy kości (jest tylko 252 różnych). To oczywiście zakłada, że ​​masz jakiś sposób, aby dowiedzieć się, jaka jest oczekiwana odpowiedź (podczas testowania jest to znane jako oracle.) Może to być referencyjna implementacja tej samej funkcji lub istoty ludzkiej. Jeśli chcesz być bardzo rygorystyczny, warto ręcznie kodować każdy oczekiwany wynik.

Tak się składa, że ​​napisałem kiedyś sztuczną inteligencję Yahtzee, która oczywiście musiała znać zasady. Kod części oceniającej wyniki można znaleźć tutaj , należy pamiętać, że implementacja dotyczy wersji skandynawskiej (Yatzy), a nasza implementacja zakłada, że ​​kostki są podawane w posortowanej kolejności.

ansjob
źródło
Pytanie za milion dolarów brzmi: czy wyprowadziłeś AI Yahtzee przy użyciu czystego TDD? Założę się, że nie możesz; Państwo mają do korzystania z wiedzy domeny, które z definicji nie jest ślepa :)
Andres F.
Tak, chyba masz rację. Jest to ogólny problem związany z TDD, polegający na tym, że przypadki testowe wymagają oczekiwanych danych wyjściowych, chyba że użytkownik chce jedynie testować nieoczekiwane awarie i nieobsługiwane wyjątki.
ansjob
0

Ten przykład naprawdę nie ma sensu. Mówimy tutaj o jednej prostej funkcji, a nie o projekcie oprogramowania. Czy to trochę skomplikowane? tak, więc to rozbicie. I absolutnie nie testujesz wszystkich możliwych danych wejściowych z 1, 1, 1, 1, 1 do 6, 6, 6, 6, 6, 6. Ta funkcja nie wymaga porządku, a jedynie kombinację, a mianowicie AAABB.

Nie potrzebujesz 200 osobnych testów logicznych. Możesz na przykład użyć zestawu. Prawie każdy język programowania ma wbudowany:

Set set;
set.add(a);
set.add(b);
set.add(c);
set.add(d);
set.add(e);

if(set.size() == 2) { // means we *must* be of the form AAAAB or AAABB.
    if(a==b==c==d) // eliminate AAAAB
        return false;
    else
        return true;
}
return false;

A jeśli dostaniesz wkład, który nie jest prawidłowym rzutem Yahtzee, powinieneś rzucić, jakby nie było jutra.

Jay Mueller
źródło