2016-12-19 1 views
0

Ich versuche, eine Reaktion/Redux-App zu erstellen, die nicht der Standard "todo" ist. Also ging ich mit 2048, es ist ein ziemlich lustiges Spiel, mit dem ich herumspielen könnte.Wie testen Sie ein Reduzierstück mit garantierter Zufälligkeit? (Redux)

Ich habe das Spiel "funktioniert" (das Spiel spielt nach den Regeln), und jetzt möchte ich retro-aktiv einige Testfälle hinzufügen, um sicherzustellen, dass ich nichts wichtiges auf der ganzen Linie (und brechen , weißt du, übe).

Ich habe ein Reduzierstück für die Platte aufgebaut, die für die unterschiedlichen Aktionen hört:

const Board = (state = 0, action) => { 
    if(state === 0){ 
     return createBoard(); 
    } 
    switch (action.type) { 
     case 'MOVE_UP': 
      return move(state, 0); 
     case 'MOVE_RIGHT': 
      return move(state, 1); 
     case 'MOVE_DOWN': 
      return move(state, 2); 
     case 'MOVE_LEFT': 
      return move(state, 3); 
     case 'NEW_GAME': 
      return createBoard(); 
     default: 
      return state; 
    } 
} 

jedoch die Bewegung (board, Richtung) Funktion führt die meisten der Schwerarbeit und ist, wo das Problem auftritt:

Mein Verständnis ist, dass Reducer Black-Box-getestet werden sollten (versuchen Sie nicht, die "mergeBoardUp" -Funktion herauszuziehen und zu testen, testen Sie den Zustand nach der Verwendung). Aufgrund der Natur des Spiels wird eine neue Kachel hinzugefügt, sobald ein gültiger Zug gespielt wird (if(distinctBoard){addNewTile}). Ich habe eine ziemlich gute Auswahl an Testfällen, die ich implementieren möchte, um Boardrotationen und -zusammenführungen zu testen, aber ich finde keine Möglichkeit, die "zufällige" Kachelanordnung zu umgehen.

Beispiel Testfall für Referenz:

it("MoveUp - simple",() => { 
    var input = [[2,0,0,0], 
       [2,4,8,16], 
       [2,4,0,16], 
       [2,4,8,0]]; 

    var inputHistory = { 
     past: [], 
     present: input, 
     future: [] 
    }; 

    var expected = [[4,8,16,32], 
        [4,4,0,0], 
        [0,0,0,0], 
        [0,0,0,0]]; 

    var rotatedLeft = Board(inputHistory, MovementButtons.moveUp()).present; 

    expect(rotatedLeft).toBe(expected); 
}) 

Die obige fehlschlägt, weil nach der gültigen bewegt, wird eine neue Kachel zufällig in dem Array platziert ist, wodurch die „erwartete“ Array immer aus um einen Wert zu sein, .

Was wäre der geeignete Ansatz zum Testen in diesem Szenario? Meine Instinkte schreien mich an, die Funktionen, die ich testen möchte, in eine Dienstprogrammdatei zu trennen (und die exportierten Funktionen daraus zu testen), aber das scheint ein Anti-Pattern zum Testen von Reduzierern zu sein.

Antwort

3

Das eigentliche Problem hier ist, dass Zufälligkeit nicht im Reducer geschehen sollte. Reduktionsgeräte sollen "reine Funktionen" ohne Nebenwirkungen sein. Reducer Code ist nicht rein wird immer noch laufen, aber Time-Travel-Debuggen wird nicht wie vorgesehen funktionieren, und wie Sie festgestellt haben, wird das Testen schwierig.

Es gibt ein paar Möglichkeiten, wie Sie eine Umstrukturierung der Dinge in Betracht ziehen könnten. Sie könnten den Großteil der Logik in den Aktionsersteller einfügen und die Reduktorlogik einfach die Daten nach Bedarf an Ort und Stelle zusammenführen lassen. Ein anderer Ansatz könnte darin bestehen, dass der Aktionsersteller die gewünschte Zufälligkeit generiert und diese in die Aktion einbezieht. Auf diese Weise bleibt der Reducer selbst rein, deterministisch und testbar.

+1

Ich möchte sicherstellen, dass ich das richtig verstehe. Anstatt den Reducer mit der Zufälligkeit umgehen zu lassen, sollte der actionCreator das zu füllende "zufällige" Kärtchen sowie das zu verschiebende Board versenden? Das klingt, als ob es mein Missverständnis löst! Ich könnte dann die Verschiebung mit einer gesetzten "zufälligen" Kachel testen! Sehr geschätzt! – Hodrobond

+0

Ja - mein Gedanke ist, dass der Action Creator tatsächlich die zufällige Kachel bestimmen könnte, _oder_ einfach die Zufallszahl oder den Startwert generieren, den der Reducer dann verwenden würde, um die Entscheidung zu treffen. Auf diese Weise ist der Reduzierer selbst deterministisch. – markerikson

0

Ich würde erneut fragen, Was versuche ich zu testen? Es sieht so aus, als könnten Sie zwei verschiedene Funktionalitäten testen: MoveUp's Merge und MoveUp's neue zufällige Reihe.

Es scheint, wie Sie könnte richtig Einheit der Merge-Funktionalität MoveUp testen einfach mit:

expect(rotatedLeft.slice(0,3)).toEqual(expected.slice(0,3)); 

Durch geeignete Wahl des Zusammenführung der drei oberen Reihen aufnehmen, würde ich sagen, dass dies als eine Einheit Test für MoveUp der Zusammenführung genügt Funktionalität.

Dann können Sie separat testen, dass die letzte Zeile zufällig ist, indem Sie überprüfen, dass jeder Wert in der letzten Zeile zu der Menge der möglichen zufälligen Werte gehört.

for (var i = 0; i < rotatedLeft[3].length; i += 1) { 
     expect([0, 2, 4]).toContain(rotatedLeft[3][i]); 
    } 
+0

Entschuldigung, ich war vielleicht eine Berührung unklar. Ich konnte eine Route zum Testen der einzelnen Funktionen bestimmen, wollte jedoch sicherstellen, dass ich "Best Practices" im Strukturieren von Reaktivierungs-/Reduxcode befolgte (die Unfähigkeit, einen Reducer zu testen, war eine riesige rote Flagge). Mein Problem war, dass der Reducer Zufälligkeit behandelte, wo er rein sein sollte (der Action Creator kann diese Logik sicher aufnehmen). – Hodrobond