Zum Hauptinhalt springen

Code-Frameworks

Mit Szenarien testen

Erstelle ein Element, führe Aktionen aus und prüfe jeden Wert und jeden Schreibvorgang.

Was ein Szenario enthält

Ein Szenario ist eine JSON-Datei in scenarios/. Es erstellt ein Element aus Eingaben, führt Aktionen der Reihe nach aus und prüft die Ergebnisse. Das Manifest listet jede Szenario-Datei auf, und jede Datei enthält ein Szenario.

scenarios/heal.json
{
  "id": "heal-after-damage",
  "framework": "keeper",
  "parameters": { "name": "Mira", "max_hp": 10 },
  "expectedValues": { "name": "Mira", "max_hp": 10, "hp": 10 },
  "actions": [
    {
      "action": "hurt",
      "parameters": { "amount": 6 },
      "expectedWrites": [{ "op": "ledger.add", "key": "hp", "value": -6 }]
    },
    {
      "action": "heal",
      "parameters": { "amount": 9 },
      "expectedWrites": [{ "op": "ledger.set", "key": "hp", "value": 10 }]
    },
    { "action": "heal", "parameters": { "amount": 3 }, "expectedWrites": [] }
  ]
}
  1. Zeilen 4-5 Baue das Element aus diesen Eingaben und prüfe seine Startwerte.
  2. Zeile 6 actions listet die Schritte, die der Reihe nach laufen.
  3. Zeile 10 Die genauen Schreibvorgänge, die der Schritt machen muss.
  4. Zeile 17 Keine Schreibvorgänge: nichts hat sich geändert.
FeldBedeutung
idDer Name des Szenarios.
frameworkDie ID des Frameworks, das erstellt werden soll.
parametersDie Eingaben, mit denen es erstellt wird.
expectedValuesJeder gespeicherte Wert direkt nach dem Erstellen.
expectedChildrenDie Werte jedes untergeordneten Platzes.
actionsAuszuführende Schritte, höchstens 32 pro Szenario. Jeder hat eine action, ihre parameters und expectedWrites.
ledgerPrüfungen abgeleiteter Werte.
listsFüllt eine Liste vorab mit Einträgen, über die Preset-ID.
expectedListsDie Einträge, die jede Liste am Ende enthält, über die Preset-ID.
poolRollsAufgezeichnete Würfe für einen Pool mit der Methode roll, nach Pool-ID. Siehe Gewürfelte Werte.
contentInhaltseinträge, die an das gebaute Element angeheftet werden (kind, entryId, name und parameters) und wie die Anheftungen eines Charakters in das Release übernommen werden.

Ein Schritt kann weitere Felder nutzen:

Schritt-FeldBedeutung
chooseFür eine Listen-Aktion: der Eintrag, den der Spieler für jede ContentRef-Eingabe wählt, über die Preset-ID.
expectedRefusalDie Ablehnung, mit der der Schritt enden muss. Der Schritt erwartet dann keine Schreibvorgänge.
expectedValuesAfterGespeicherte und abgeleitete Werte nach dem Schritt. Abgeleitete Werte werden zuerst neu berechnet.

Schreibvorgänge

expectedWrites listet jeden Schreibvorgang des Schritts der Reihe nach auf. Nutze ledger.set für einen gesetzten Wert und ledger.add für einen addierten Betrag. Ein Schritt, der nichts ändert, listet keine Schreibvorgänge auf, damit auch die Bedingung getestet wird, die einen Schreibvorgang überspringt.

Weichen die echten Schreibvorgänge ab, zeigt die Prüfung beide Listen.

"expectedWrites": [{ "op": "ledger.set", "key": "hp", "value": 9 }]
Expected ordered writes [{"op":"ledger.set","key":"hp","value":9}]; observed [{"op":"ledger.set","key":"hp","value":10}].

Abgeleitete Werte und untergeordnete Elemente

Die Liste ledger prüft eine Ledger-Funktion. Sie nennt die Funktions-ID, den Wert, von dem sie ausgeht, und den Wert, den sie zurückgeben muss. expectedChildren prüft die untergeordneten Elemente.

scenarios/new-keeper.json
  "expectedChildren": { "lamp": { "name": "Lantern", "oil": 5 } },
  1. Zeile 17 Prüft die Elemente in den untergeordneten Plätzen.
scenarios/new-keeper.json
  "ledger": [{ "function": "guard", "currentValue": 0, "expected": 3 }]
  1. Zeile 18 Ruft die Schutzfunktion mit dem aktuellen Wert 0 auf und erwartet 3.

Was abgedeckt sein muss

  • Jeder Konstruktor, jede Aktion und jede Ledger-Funktion braucht ein Szenario.
  • Jede Aktion braucht mindestens einen Schritt. Füge für jeden Zweig, den du beweisen willst, einen Schritt hinzu, zum Beispiel einen, der schreibt, und einen, der nichts schreibt.
  • Eine Aktion, die zu einer Liste etwas hinzufügt, braucht einen akzeptierten Schritt und einen Schritt mit expectedRefusal, egal was ihr Angebot sagt. Der abgelehnte Schritt beweist, dass das Angebot Nein sagen kann. Eine Aktion, die aus einer Liste etwas entfernt, braucht einen akzeptierten Schritt.
  • Die erwarteten Werte müssen zu dem passen, was der Konstruktor wirklich zurückgibt, mit bereits angewendeten Ledger-Funktionen.

Schritte sind nach ihrem Szenario benannt. Eine Meldung wie heal-after-damage-2 meint den dritten Schritt von heal-after-damage, denn die Zahl zählt ab 0. Andere Meldungen zählen Schritte ab 1.

Ein Schritt, der eine Ablehnung erwartet, obwohl die Aktion nicht abgelehnt wurde, schlägt fehl. Das Gegenteil schlägt auch fehl:

the step expects it to be refused with NOT_OFFERED
Add expectedRefusal

Eine Aktion ohne Schritt schlägt fehl mit:

SELF_SCALAR_ACTION_SCENARIO_COVERAGE_MISSING: action keeper.hurt (Take damage) has no executable scenario steps.

Eine Listen-Aktion ohne akzeptierten oder ohne abgelehnten Schritt schlägt fehl mit:

add a step with expectedRefusal, so its refused branch is proven.
add a step whose list.add is accepted, so its accepted branch is proven.

Erwartete Werte, die vom Konstruktor abweichen, schlagen fehl mit:

Constructor output does not match its scenario.

Ablehnungen

Eine Listen-Aktion kann abgelehnt werden. Ein Schritt, der das erwartet, nennt den Code in expectedRefusal und listet keine Schreibvorgänge auf.

CodeWann
NOT_OFFEREDDer gewählte Eintrag gehört nicht zu den Einträgen, die das Angebot erlaubt.
ALREADY_IN_LISTDie Liste enthält den Eintrag schon.
LIST_FULLDie Liste hat ihre maximale Größe erreicht.
NOT_IN_LISTlist.remove nennt einen Eintrag, den die Liste nicht enthält.
LIST_CAPEin Limit, das die Liste deklariert, würde überschritten.
SAME_NAMEDie Liste enthält schon einen Eintrag mit diesem Namen.

Dieses Projekt beweist mehrere davon. Es hat auch einen Schritt, in dem eine Schutzbedingung die Aktion stoppt, also erwartet der Schritt keine Schreibvorgänge und keine Ablehnung.

scenarios/pack.json
{
  "id": "pack-trinkets",
  "framework": "keeper",
  "parameters": { "name": "Mira", "max_hp": 10 },
  "expectedValues": { "name": "Mira", "max_hp": 10, "hp": 10 },
  "actions": [
    {
      "action": "pack_trinket",
      "parameters": {},
      "choose": { "trinket": "rope" },
      "expectedWrites": [{ "op": "list.add", "key": "pack", "value": "rope" }]
    },
    {
      "action": "pack_trinket",
      "parameters": {},
      "choose": { "trinket": "rope" },
      "expectedRefusal": "ALREADY_IN_LIST",
      "expectedWrites": []
    },
    {
      "action": "pack_trinket",
      "parameters": {},
      "choose": { "trinket": "anvil" },
      "expectedRefusal": "NOT_OFFERED",
      "expectedWrites": []
    },
    {
      "action": "pack_trinket",
      "parameters": {},
      "choose": { "trinket": "candle" },
      "expectedWrites": [
        { "op": "list.add", "key": "pack", "value": "candle" }
      ]
    },
    {
      "action": "pack_trinket",
      "parameters": {},
      "choose": { "trinket": "bell" },
      "expectedWrites": []
    },
    {
      "action": "unpack_trinket",
      "parameters": {},
      "choose": { "trinket": "rope" },
      "expectedWrites": [
        { "op": "list.remove", "key": "pack", "value": "rope" }
      ]
    },
    {
      "action": "unpack_trinket",
      "parameters": {},
      "choose": { "trinket": "rope" },
      "expectedRefusal": "NOT_IN_LIST",
      "expectedWrites": []
    }
  ],
  "expectedLists": { "pack": ["candle"] }
}
  1. Zeilen 7-12 Akzeptiert: list.add schreibt das Seil.
  2. Zeilen 13-19 Abgelehnt: Die Liste enthält das Seil schon.
  3. Zeilen 20-26 Abgelehnt: Das Angebot erlaubt den Amboss nicht.
  4. Zeilen 35-40 Der Packen enthält schon zwei Einträge, also schreibt die Schutzbedingung nichts und es wird keine Ablehnung erwartet.
  5. Zeilen 41-48 Entfernt: Das Seil verlässt die Liste.
  6. Zeilen 49-55 Abgelehnt: Das Seil ist nicht mehr in der Liste.
  7. Zeile 57 Der Packen endet nur mit der Kerze.

Szenarien ausführen

Prüfen und Vorschau in The Forge kompiliert das Projekt und führt jedes Szenario aus. Nichts wird veröffentlicht, und kein laufender Charakter ändert sich.

Weiter