Wie solltest du ein Yahtzee-Spiel TDD?

36

Angenommen, Sie schreiben einen TDD-Stil für ein Yahtzee-Spiel. Sie möchten den Teil des Codes testen, der bestimmt, ob ein Satz mit fünf Würfeln ein volles Haus ist oder nicht. Soweit ich weiß, befolgen Sie bei TDD folgende Grundsätze:

  • Schreiben Sie zuerst die Tests
  • Schreiben Sie das Einfachste, was funktioniert
  • Verfeinern und umgestalten

Ein erster Test könnte also so aussehen:

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);
}

Wenn Sie dem Befehl "Schreiben Sie so einfach IsFullHousewie möglich" folgen, sollten Sie die Methode folgendermaßen schreiben :

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;
}

Dies führt zu einem grünen Test, aber die Implementierung ist unvollständig.

Sollten Sie jede mögliche gültige Kombination (sowohl von Werten als auch von Positionen) für ein volles Haus testen? Das scheint die einzige Möglichkeit zu sein, absolut sicher zu sein, dass Ihr IsFullHouseCode vollständig getestet und korrekt ist, aber es klingt auch ziemlich verrückt, dies zu tun.

Wie würden Sie so etwas testen?

Aktualisieren

Erik und Kilian weisen darauf hin, dass die Verwendung von Literalen in der ersten Implementierung für einen Grüntest möglicherweise nicht die beste Idee ist. Ich möchte erklären, warum ich das getan habe und diese Erklärung passt nicht in einen Kommentar.

Meine praktischen Erfahrungen mit Unit-Tests (insbesondere mit einem TDD-Ansatz) sind sehr begrenzt. Ich erinnere mich, eine Aufnahme von Roy Osheroves TDD Masterclass auf Tekpub gesehen zu haben. In einer der Folgen baut er einen String Calculator TDD-Stil auf. Die vollständige Spezifikation des String-Rechners finden Sie hier: http://osherove.com/tdd-kata-1/

Er beginnt mit einem Test wie diesem:

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

    Assert.AreEqual(0, result);
}

Dies führt zu dieser ersten Implementierung der AddMethode:

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

Dann wird dieser Test hinzugefügt:

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

    Assert.AreEqual(1, result);
}

Und die AddMethode wurde überarbeitet:

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

    return 1;
}

Nach jedem Schritt sagt Roy "Schreibe das Einfachste, was funktionieren wird".

Daher dachte ich, ich würde diesen Ansatz ausprobieren, wenn ich ein Yahtzee-Spiel im TDD-Stil spielen wollte.

Kristof Claes
quelle
8
"Schreiben Sie so einfach wie möglich, was funktioniert" ist eigentlich eine Abkürzung; Der richtige Rat lautet: "Schreiben Sie das einfachste, was möglich ist und das offensichtlich nicht richtig funktioniert." Also, nein, du solltest nicht schreibenif (roll1 == 1 && roll2 == 1 && roll3 == 1 && roll4 == 2 && roll5 == 2)
Carson63000
3
Vielen Dank, dass Sie Eriks Antwort zusammengefasst haben, sei es auf weniger argumentative oder zivilisierte Weise.
Kristof Claes
1
"Schreiben Sie das Einfachste, was funktioniert", wie @ Carson63000, ist eigentlich eine Vereinfachung. Es ist tatsächlich gefährlich, so zu denken. es führt zum berüchtigten Sudoku TDD-Debakel (google it). Wenn man es blind betrachtet, ist TDD in der Tat ein Kopfzerbrechen: Sie können einen nicht-trivialen Algorithmus nicht verallgemeinern, indem Sie blind "das Einfachste tun, was funktioniert" ... Sie müssen tatsächlich denken! Leider folgen sogar mutmaßliche Meister von XP und TDD manchmal blindlings ...
Andres F.
1
@AndresF. Beachten Sie, dass Ihr Kommentar in der Google-Suche nach weniger als drei Tagen höher war als ein Großteil des Kommentars zum "Soduko TDD-Debakel". Trotzdem, wie man ein Sudoku nicht löst, fasste es zusammen: TDD steht für Qualität, nicht für Korrektheit. Sie müssen den Algorithmus lösen, bevor Sie mit dem Codieren beginnen, insbesondere mit TDD. (Nicht, dass ich auch kein Code-Erstprogrammierer bin.)
Mark Hurd
1
pvv.org/~oma/TDDinC_Yahtzee_27oct2011.pdf könnte von Interesse sein.

Antworten:

40

Es gibt bereits viele gute Antworten auf diese Frage, und ich habe einige von ihnen kommentiert und bewertet. Dennoch möchte ich einige Gedanken hinzufügen.

Flexibilität ist nichts für Anfänger

Das OP gibt eindeutig an, dass er keine Erfahrung mit TDD hat, und ich denke, eine gute Antwort muss dies berücksichtigen. In der Terminologie des Dreyfus-Modells für den Erwerb von Fähigkeiten ist er wahrscheinlich ein Neuling . Es ist nichts Falsches daran, Anfänger zu sein - wir sind alle Anfänger, wenn wir anfangen, etwas Neues zu lernen. Das Dreyfus-Modell erklärt jedoch, dass Anfänger durch gekennzeichnet sind

  • starre Einhaltung gelehrter Regeln oder Pläne
  • keine Ermessensentscheidung

Das ist keine Beschreibung eines Persönlichkeitsmangels, daher gibt es keinen Grund, sich dafür zu schämen - es ist eine Phase, die wir alle durchlaufen müssen, um etwas Neues zu lernen.

Dies gilt auch für TDD.

Obwohl ich vielen anderen Antworten hier zustimme, dass TDD nicht dogmatisch sein muss und dass es manchmal vorteilhafter sein kann, auf alternative Weise zu arbeiten, hilft das niemandem, wenn er gerade erst anfängt. Wie können Sie diskretionär urteilen, wenn Sie keine Erfahrung haben?

Wenn ein Anfänger den Rat annimmt, dass es manchmal in Ordnung ist, kein TDD zu machen, wie kann er feststellen, wann es in Ordnung ist, das TDD zu überspringen?

Ohne Erfahrung oder Anleitung kann ein Anfänger TDD nur jedes Mal verlassen, wenn es zu schwierig wird. Das ist die menschliche Natur, aber kein guter Weg, um zu lernen.

Hören Sie sich die Tests an

Wenn es immer schwieriger wird, TDD zu verlassen, müssen Sie sich einen der wichtigsten Vorteile von TDD entgehen lassen. Tests bieten frühzeitiges Feedback zur API des SUT. Wenn der Test schwer zu schreiben ist, ist dies ein wichtiges Zeichen dafür, dass das SUT schwer zu verwenden ist.

Aus diesem Grund lautet eine der wichtigsten Botschaften von GOOS : Hören Sie auf Ihre Tests!

Bei dieser Frage war meine erste Reaktion auf die vorgeschlagene API des Yahtzee-Spiels und die Diskussion über die Kombinatorik auf dieser Seite, dass dies ein wichtiges Feedback zur API ist.

Muss die API Würfelwürfe als geordnete Folge von ganzen Zahlen darstellen? Für mich dieser Geruch von primitiver Besessenheit . Deshalb freute ich mich über die Antwort von tallseth, die die Einführung einer RollKlasse vorschlug . Ich denke, das ist ein ausgezeichneter Vorschlag.

Ich denke jedoch, dass einige der Kommentare zu dieser Antwort falsch sind. Was TDD dann vorschlägt, ist, dass, sobald Sie die Idee haben, dass eine RollKlasse eine gute Idee wäre, Sie die Arbeit am ursprünglichen SUT unterbrechen und beginnen, die RollKlasse mit TDD zu bearbeiten .

Ich stimme zu, dass TDD mehr auf den „glücklichen Weg“ als auf umfassende Tests abzielt, aber es hilft trotzdem, das System in verwaltbare Einheiten zu zerlegen. Eine RollKlasse klingt wie etwas, das man viel einfacher zu Ende bringen könnte.

Sobald die RollKlasse ausreichend entwickelt ist, würden Sie zum ursprünglichen SUT zurückkehren und es in Bezug auf RollEingaben ausarbeiten .

Der Vorschlag eines Test-Helfers impliziert nicht notwendigerweise Zufälligkeit - es ist nur eine Möglichkeit, den Test lesbarer zu machen.

Eine andere Möglichkeit, Eingaben in Bezug auf RollInstanzen zu analysieren und zu modellieren, wäre die Einführung eines Test Data Builder .

Rot / Grün / Refaktor ist ein dreistufiger Prozess

Obwohl ich der allgemeinen Meinung zustimme, dass (wenn Sie über ausreichende Erfahrung mit TDD verfügen) Sie sich nicht strikt an TDD halten müssen, denke ich, dass dies bei einer Yahtzee-Übung ein ziemlich schlechter Rat ist. Obwohl ich die Details der Yahtzee-Regeln nicht kenne, sehe ich hier kein überzeugendes Argument dafür, dass Sie sich nicht rigoros an den Red / Green / Refactor-Prozess halten und dennoch ein angemessenes Ergebnis erzielen können.

Was die meisten hier zu vergessen scheinen, ist die dritte Stufe des Rot / Grün / Refaktor-Prozesses. Zuerst schreibst du den Test. Dann schreiben Sie die einfachste Implementierung, die alle Tests besteht. Dann überarbeiten Sie.

Hier, in diesem dritten Zustand, können Sie all Ihre beruflichen Fähigkeiten unter Beweis stellen. Hier dürfen Sie über den Code nachdenken.

Ich denke jedoch, es ist eine Ausrede, zu behaupten, dass Sie nur "das einfachste schreiben sollten, was möglich ist, und das nicht völlig geisteskrank und offensichtlich falsch , was funktioniert". Wenn Sie (glaube ich) im Voraus genug über die Implementierung wissen, wird alles , was nicht zur vollständigen Lösung gehört, offensichtlich falsch sein . Was den Rat betrifft, ist dies für einen Anfänger ziemlich nutzlos.

Was wirklich passieren sollte, ist, dass, wenn Sie alle Tests mit einer offensichtlich falschen Implementierung bestehen können, dies die Rückmeldung ist, dass Sie einen weiteren Test schreiben sollten .

Es ist überraschend, wie oft Sie dies tun, um eine völlig andere Implementierung zu erreichen als die, an die Sie zuerst gedacht hatten. Manchmal ist die Alternative, die so wächst, besser als Ihr ursprünglicher Plan.

Strenge ist ein Lernwerkzeug

Es ist sehr sinnvoll, sich an strenge Prozesse wie Rot / Grün / Refaktor zu halten, solange man lernt. Es zwingt den Lernenden, Erfahrung mit TDD zu sammeln, nicht nur, wenn es einfach ist, sondern auch, wenn es schwierig ist.

Nur wenn Sie alle harten Teile gemeistert haben, sind Sie in der Lage, eine fundierte Entscheidung darüber zu treffen, wann Sie vom „wahren“ Pfad abweichen sollten. Dann beginnt man seinen eigenen Weg zu finden.

Mark Seemann
quelle
Ein anderer TDD-Neuling hier, mit all den üblichen Bedenken, es zu versuchen. Interessant zu nehmen, wenn Sie alle Tests mit einer offensichtlich falschen Implementierung bestehen können, ist das Feedback, dass Sie einen weiteren Test schreiben sollten. Scheint ein guter Weg zu sein, um die Wahrnehmung in den Griff zu bekommen, dass das Testen der "Braindead" -Implementierungen überflüssig ist.
Shambulator
1
Wow Danke. Ich habe große Angst vor der Tendenz der Leute, TDD-Anfängern (oder anderen Disziplinen) zu sagen, sie sollten sich keine Gedanken über die Regeln machen, sondern nur das tun, was sich am besten anfühlt. Wie können Sie wissen, was sich am besten anfühlt, wenn Sie keine Kenntnisse oder Erfahrungen haben? Ich möchte auch das Prinzip der Transformationspriorität erwähnen, oder der Code sollte generischer werden, wenn die Tests spezifischer werden. Die hartnäckigsten TDD-Unterstützer wie Onkel Bob würden nicht hinter dem Gedanken stehen, "für jeden Test einfach eine neue if-Anweisung einzufügen".
Sara
41

Als Haftungsausschluss ist dies TDD, wie ich es praktiziere, und wie Kilian treffend hervorhebt, wäre ich vorsichtig mit jedem, der vorschlug, dass es einen richtigen Weg gibt, es zu praktizieren. Aber vielleicht hilft es dir ...

Das Einfachste, was Sie tun können, um Ihren Test zu bestehen, ist Folgendes:

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

Dies ist wichtig, da dies nicht auf eine TDD-Praxis zurückzuführen ist, sondern weil Hardcording in all diesen Literalen keine wirklich gute Idee ist. Eines der schwierigsten Dinge, die Sie mit TDD tun können, ist, dass es keine umfassende Teststrategie ist - es ist eine Möglichkeit, sich vor Regressionen zu schützen und Fortschritte zu markieren, während Sie den Code einfach halten. Es ist eine Entwicklungsstrategie und keine Teststrategie.

Der Grund, warum ich diesen Unterschied erwähne, ist, dass er Ihnen hilft, die Tests zu bestimmen, die Sie schreiben sollten. Die Antwort auf "Welche Tests soll ich schreiben?" lautet: "Welche Tests auch immer Sie benötigen, um den Code so abzurufen, wie Sie ihn möchten." Stellen Sie sich TDD als eine Möglichkeit vor, Algorithmen und Gründe für Ihren Code herauszufinden. Welcher Test kommt angesichts Ihres Tests und meiner "einfachen grünen" Implementierung als Nächstes? Nun, Sie haben etwas etabliert, das ein volles Haus ist. Wann ist es also kein volles Haus?

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);
}

Nun müssen Sie einen Weg finden, um zwischen den beiden Testfällen zu unterscheiden, der von Bedeutung ist . Ich persönlich würde ein bisschen klärende Informationen dazu verwenden, "das Einfachste zu tun, um den Test zu bestehen" und zu sagen, "das Einfachste zu tun, um den Test zu bestehen, der Ihre Implementierung fördert". Das Schreiben fehlgeschlagener Tests ist Ihr Vorwand, um den Code zu ändern. Wenn Sie also jeden Test schreiben, sollten Sie sich fragen: "Was macht mein Code nicht, was soll er und wie kann ich diesen Mangel aufdecken?" Es kann Ihnen auch dabei helfen, Ihren Code robust zu machen und Edge Cases zu verarbeiten. Was machst du, wenn ein Anrufer Unsinn eingibt?

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....
}

Zusammenfassend lässt sich sagen, dass Sie beim Testen jeder Wertekombination mit ziemlicher Sicherheit etwas falsch machen (und wahrscheinlich mit einer kombinatorischen Explosion von Bedingungen enden werden). Wenn es um TDD geht, sollten Sie die Mindestanzahl an Testfällen schreiben, die erforderlich sind, um den gewünschten Algorithmus zu erhalten. Alle weiteren Tests, die Sie schreiben, beginnen grün und werden somit im Wesentlichen zur Dokumentation und sind nicht strikter Bestandteil des TDD-Prozesses. Sie werden nur dann weitere TDD-Testfälle schreiben, wenn sich die Anforderungen ändern oder ein Fehler aufgedeckt wird. In diesem Fall werden Sie den Mangel mit einem Test dokumentieren und ihn dann bestehen lassen.

Aktualisieren:

Ich habe dies als Kommentar zu Ihrem Update begonnen, aber es hat ziemlich lange gedauert ...

Ich würde sagen, das Problem liegt nicht in der Existenz von Literalen, Punkt, sondern darin, dass das 'Einfachste' eine 5-teilige Bedingung ist. Wenn Sie darüber nachdenken, ist eine 5-teilige Bedingung eigentlich ziemlich kompliziert. Es ist üblich, Literale während des Rot-Grün-Schritts zu verwenden und sie dann zu Konstanten im Refactor-Schritt zu abstrahieren oder sie in einem späteren Test zu verallgemeinern.

Während meiner eigenen Reise mit TDD wurde mir klar, dass eine wichtige Unterscheidung getroffen werden muss - es ist nicht gut, "einfach" und "stumpf" zu verwechseln. Das heißt, als ich anfing, sah ich zu, wie Leute TDD machten, und ich dachte, "sie tun nur das Dümmste, was möglich ist, um die Tests zu bestehen", und ahmte das eine Weile nach, bis mir klar wurde, dass "einfach" auf subtile Weise anders war als "stumpf". Manchmal überlappen sie sich, aber oft nicht.

Entschuldigung, wenn ich den Eindruck erweckte, dass die Existenz von Literalen das Problem war - ist es nicht. Ich würde sagen, die Komplexität der Bedingung mit den 5 Sätzen ist das Problem. Ihr erstes Rot zu Grün kann einfach "wahr" sein, weil das wirklich einfach (und zufällig stumpf) ist. Der nächste Testfall mit (1, 2, 3, 4, 5) muss false zurückgeben, und hier beginnt man, "stumpf" zurückzulassen. Sie müssen sich fragen: "Warum ist (1, 1, 1, 2, 2) ein volles Haus und (1, 2, 3, 4, 5) nicht?" Das Einfachste, was Sie sich vorstellen können, ist, dass eines das letzte Sequenzelement 5 oder das zweite Sequenzelement 2 hat und das andere nicht. Diese sind einfach, aber auch (unnötig) stumpf. Was Sie wirklich fahren wollen, ist "wie viele der gleichen Nummer haben sie?" Sie können also den zweiten Test bestehen, indem Sie prüfen, ob eine Wiederholung vorliegt oder nicht. In der einen mit einer Wiederholung hast du ein volles Haus und in der anderen nicht. Jetzt besteht der Test und Sie schreiben einen weiteren Testfall, der eine Wiederholung aufweist, aber kein komplettes Haus ist, um Ihren Algorithmus weiter zu verfeinern.

Sie können dies mit Literalen tun oder auch nicht, und es ist in Ordnung, wenn Sie dies tun. Die allgemeine Idee ist jedoch, Ihren Algorithmus "organisch" zu erweitern, wenn Sie weitere Fälle hinzufügen.

Erik Dietrich
quelle
Ich habe meine Frage aktualisiert, um weitere Informationen darüber hinzuzufügen, warum ich mit dem wörtlichen Ansatz begonnen habe.
Kristof Claes
9
Das ist eine großartige Antwort.
Tallseth
1
Vielen Dank für Ihre nachdenkliche und gut erläuterte Antwort. Es macht jetzt tatsächlich sehr viel Sinn, wenn ich darüber nachdenke.
Kristof Claes
1
Gründliches Testen bedeutet nicht, jede Kombination zu testen ... Das ist albern. Nehmen Sie für diesen speziellen Fall ein bestimmtes volles Haus oder zwei und ein paar nicht volle Häuser. Auch alle speziellen Kombinationen, die Probleme verursachen könnten (zB 5 von einer Art).
Schleis
3
+1 Die Prinzipien hinter dieser Antwort werden von Robert C. Martins Transformation Priority Premise cleancoder.posterous.com/the-transformation-priority-premise
Mark Seemann
5

Das Testen von fünf bestimmten Literalwerten in einer bestimmten Kombination ist für mein fiebriges Gehirn nicht "einfach". Wenn die Lösung für ein Problem wirklich offensichtlich ist (zählen Sie, ob Sie genau drei und genau zwei von einem beliebigen Wert haben), gehen Sie auf jeden Fall vor und codieren Sie diese Lösung und schreiben Sie einige Tests, mit denen Sie sehr, sehr unwahrscheinlich aus Versehen zufrieden sein werden die Menge an Code, die Sie geschrieben haben (dh unterschiedliche Literale und unterschiedliche Ordnungen der Tripel und Doppelten).

TDD-Maximen sind wirklich Werkzeuge, keine religiösen Überzeugungen. Ihr Ziel ist es, Sie dazu zu bringen, schnell korrekten, gut faktorisierten Code zu schreiben. Wenn dem offensichtlich eine Maxime im Wege steht, springen Sie einfach voran und fahren Sie mit dem nächsten Schritt fort. In Ihrem Projekt gibt es viele nicht offensichtliche Stellen, an denen Sie es anwenden können.

Kilian Foth
quelle
5

Eriks Antwort ist großartig, aber ich dachte, ich könnte einen Trick beim Testschreiben teilen.

Beginnen Sie mit diesem Test:

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

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

Dieser Test wird noch besser, wenn Sie eine RollKlasse erstellen , anstatt 5 Parameter zu übergeben:

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

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

Das gibt diese Implementierung:

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

Dann schreibe diesen Test:

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

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

Sobald das vorbei ist, schreibe folgendes:

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

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

Danach brauchst du bestimmt nicht mehr zu schreiben (vielleicht zwei Paare oder yahtzee, wenn du denkst, dass es kein Full House ist).

Implementieren Sie natürlich Ihre Any-Methoden, um zufällige Rolls zurückzugeben, die Ihren Kriterien entsprechen.

Dieser Ansatz bietet einige Vorteile:

  • Sie müssen keinen Test schreiben, dessen einziger Zweck es ist, zu verhindern, dass Sie bei bestimmten Werten hängen bleiben
  • Die Tests kommunizieren Ihre Absicht sehr gut (der Code des ersten Tests schreit: "Jedes Full House kehrt wahr zurück").
  • Es bringt Sie schnell zum Punkt, an dem Sie das Fleisch des Problems bearbeiten können
  • manchmal werden Fälle bemerkt, an die Sie nicht gedacht haben
tallseth
quelle
Wenn Sie diesen Ansatz wählen, müssen Sie Ihre Protokollnachrichten in Ihren Assert.That-Anweisungen verbessern. Der Entwickler muss sehen, welche Eingabe den Fehler verursacht hat.
Bringer128
Erzeugt dies nicht ein Henne-oder-Ei-Dilemma? Wenn Sie AnyFullHouse implementieren (auch mit TDD), benötigen Sie IsFullHouse nicht, um die Richtigkeit zu überprüfen? Insbesondere wenn AnyFullHouse einen Fehler aufweist, könnte dieser Fehler in IsFullHouse repliziert werden.
Seidenschwanz
AnyFullHouse () ist eine Methode in einem Testfall. Übertragen Sie Ihre Testfälle normalerweise auf TDD? Nein. Es ist auch viel einfacher, ein zufälliges Exemplar eines vollen Hauses (oder eines anderen Würfels) zu erstellen, als es auf seine Existenz zu testen. Wenn Ihr Test einen Fehler aufweist, könnte er natürlich im Produktionscode repliziert werden. Das gilt allerdings für jeden Test.
Tallseth
AnyFullHouse ist eine "Hilfsmethode" in einem Testfall. Wenn sie allgemein genug sind, lassen Sie auch Hilfsmethoden testen!
Mark Hurd
Sollte IsFullHousewirklich zurückkehren, truewenn pairNum == trioNum ?
recursion.ninja
2

Ich kann mir zwei Hauptmethoden vorstellen, die ich beim Testen in Betracht ziehen würde.

  1. Fügen Sie "einige" weitere Testfälle (~ 5) von gültigen Full-House-Sets hinzu, und die gleiche Anzahl von erwarteten Fehlern ({1, 1, 2, 3, 3} ist eine gute. Denken Sie daran, dass es beispielsweise 5 sein könnten als "3 desselben plus ein Paar" durch eine falsche Implementierung erkannt). Bei dieser Methode wird davon ausgegangen, dass der Entwickler nicht nur versucht, die Tests zu bestehen, sondern sie tatsächlich korrekt umzusetzen.

  2. Testen Sie alle möglichen Würfelsätze (es gibt nur 252 verschiedene). Dies setzt natürlich voraus, dass Sie eine Möglichkeit haben zu wissen, wie die erwartete Antwort lautet (beim Testen wird dies als bezeichnet oracle). Dies könnte eine Referenzimplementierung derselben Funktion oder ein Mensch sein. Wenn Sie wirklich streng sein möchten, kann es sich lohnen, jedes erwartete Ergebnis manuell zu codieren.

Zufällig habe ich einmal eine Yahtzee-KI geschrieben, die natürlich die Regeln kennen musste. Den Code für den Teil zur Bewertung der Punktzahl finden Sie hier . Bitte beachten Sie, dass die Implementierung für die skandinavische Version (Yatzy) vorgesehen ist. Bei unserer Implementierung wird davon ausgegangen, dass die Würfel in sortierter Reihenfolge ausgegeben werden.

ansjob
quelle
Die Millionen-Dollar-Frage lautet: Haben Sie die Yahtzee AI mit reinem TDD abgeleitet? Meine Wette ist, dass Sie nicht können; Sie müssen Domain-Wissen verwenden, das per Definition nicht blind ist :)
Andres F.
Ja, ich denke du hast recht. Dies ist ein allgemeines Problem bei TDD, da die Testfälle erwartete Ausgaben benötigen, es sei denn, Sie möchten nur auf unerwartete Abstürze und unbehandelte Ausnahmen testen.
Ansjob
0

Dieses Beispiel geht wirklich am Rande vorbei. Wir sprechen hier von einer einfachen Funktion, nicht von einem Software-Design. Ist es ein bisschen kompliziert? ja, also machst du es kaputt. Und Sie testen auf keinen Fall jede mögliche Eingabe von 1, 1, 1, 1, 1 bis 6, 6, 6, 6, 6, 6. Die betreffende Funktion erfordert keine Reihenfolge, sondern nur eine Kombination, nämlich AAABB.

Sie benötigen keine 200 separaten Logiktests. Sie könnten zum Beispiel ein Set verwenden. In fast jeder Programmiersprache ist Folgendes integriert:

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;

Und wenn du eine Eingabe erhältst, die kein gültiger Yahtzee-Wurf ist, solltest du werfen, als gäbe es kein Morgen.

Jay Mueller
quelle