Aller au contenu principal

Frameworks de code

Tester avec des scénarios

Construisez un Élément, exécutez des actions et vérifiez chaque valeur et chaque écriture.

Ce que contient un scénario

Un scénario est un fichier JSON dans scenarios/. Il construit un Élément à partir d'entrées, exécute des actions dans l'ordre et vérifie les résultats. Le manifeste liste chaque fichier de scénario, et chaque fichier contient un scénario.

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. Lignes 4-5 Construisez l'Élément avec ces entrées et vérifiez ses valeurs de départ.
  2. Ligne 6 actions liste les étapes à exécuter, dans l'ordre.
  3. Ligne 10 Les écritures exactes que l'étape doit faire.
  4. Ligne 17 Aucune écriture : rien n'a changé.
ChampSignification
idLe nom du scénario.
frameworkL'id du Framework à construire.
parametersLes entrées avec lesquelles le construire.
expectedValuesChaque valeur stockée juste après la construction.
expectedChildrenLes valeurs de chaque emplacement enfant.
actionsLes étapes à exécuter, 32 au plus par scénario. Chacune a une action, ses paramètres et expectedWrites.
ledgerDes vérifications de valeurs dérivées.
listsRemplit une liste d'entrées au départ, par id de préréglage.
expectedListsLes entrées que chaque liste contient à la fin, par id de préréglage.
poolRollsLancers enregistrés pour un pool qui utilise la méthode roll, par identifiant de pool. Voir Scores lancés.
contentEntrées de contenu épinglées à l'Element construit (kind, entryId, name et parameters), fusionnées dans la version comme les épingles d'un personnage.

Une étape peut utiliser d'autres champs :

Champ d'étapeSignification
choosePour une action de liste : l'entrée que le joueur choisit pour chaque entrée ContentRef, par id de préréglage.
expectedRefusalLe refus par lequel l'étape doit se terminer. L'étape n'attend alors aucune écriture.
expectedValuesAfterLes valeurs stockées et dérivées après l'étape. Les valeurs dérivées sont d'abord recalculées.

Écritures

expectedWrites liste chaque écriture de l'étape, dans l'ordre. Utilisez ledger.set pour une valeur définie et ledger.add pour un montant ajouté. Une étape qui ne change rien ne liste aucune écriture : la garde qui évite une écriture est donc testée aussi.

Si les écritures réelles diffèrent, la vérification montre les deux listes.

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

Valeurs dérivées et enfants

La liste ledger vérifie une fonction de registre. Elle donne l'id de la fonction, la valeur de départ et la valeur qu'elle doit retourner. expectedChildren vérifie les Éléments enfants.

scenarios/new-keeper.json
  "expectedChildren": { "lamp": { "name": "Lantern", "oil": 5 } },
  1. Ligne 17 Vérifie les Éléments dans les emplacements enfants.
scenarios/new-keeper.json
  "ledger": [{ "function": "guard", "currentValue": 0, "expected": 3 }]
  1. Ligne 18 Appelle la fonction de garde avec la valeur actuelle 0 et attend 3.

Ce qui doit être couvert

  • Chaque constructeur, chaque action et chaque fonction de registre a besoin d'un scénario.
  • Chaque action a besoin d'au moins une étape. Ajoutez une étape pour chaque branche à prouver, par exemple une qui écrit et une qui n'écrit rien.
  • Une action qui ajoute à une liste a besoin d'une étape acceptée et d'une étape avec expectedRefusal, quoi que dise son offre. L'étape refusée prouve que l'offre peut dire non. Une action qui ne fait que retirer d'une liste a besoin d'une étape acceptée.
  • Les valeurs attendues doivent correspondre à ce que le constructeur retourne vraiment, une fois les fonctions de registre appliquées.

Les étapes portent le nom de leur scénario. Un message comme heal-after-damage-2 désigne la troisième étape de heal-after-damage, car le numéro compte à partir de 0. Les autres messages comptent les étapes à partir de 1.

Une étape qui attend un refus d'une action qui n'a pas été refusée échoue, et l'inverse aussi :

the step expects it to be refused with NOT_OFFERED
Add expectedRefusal

Une action sans étape échoue avec :

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

Une action de liste sans étape acceptée ou sans étape refusée échoue avec :

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.

Des valeurs attendues différentes de celles du constructeur échouent avec :

Constructor output does not match its scenario.

Refus

Une action de liste peut être refusée. Une étape qui l'attend nomme le code dans expectedRefusal, et ne liste aucune écriture.

CodeQuand
NOT_OFFEREDL'entrée choisie ne fait pas partie de celles que l'offre autorise.
ALREADY_IN_LISTLa liste contient déjà l'entrée.
LIST_FULLLa liste a atteint sa taille maximale.
NOT_IN_LISTlist.remove nomme une entrée que la liste ne contient pas.
LIST_CAPUne limite que la liste déclare serait dépassée.
SAME_NAMELa liste contient déjà une entrée portant ce nom.

Ce projet en prouve plusieurs. Il a aussi une étape où une garde arrête l'action, donc l'étape n'attend ni écriture ni refus.

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. Lignes 7-12 Acceptée : list.add écrit la corde.
  2. Lignes 13-19 Refusée : la liste contient déjà la corde.
  3. Lignes 20-26 Refusée : l'offre n'autorise pas l'enclume.
  4. Lignes 35-40 Le sac contient déjà deux entrées, donc la garde n'écrit rien et aucun refus n'est attendu.
  5. Lignes 41-48 Retirée : la corde quitte la liste.
  6. Lignes 49-55 Refusée : la corde n'est plus dans la liste.
  7. Ligne 57 Le sac se termine avec la bougie seulement.

Les exécuter

Vérifier et prévisualiser dans The Forge compile le projet et exécute chaque scénario. Rien n'est publié, et aucun personnage en ligne ne change.

Suite