Tesztgenerátorok


A tesztgenerátor egy kurzusspecifikus szoftver, amely automatikusan előállítja egy gyakorlófeladat tesztjeit. Ezt úgy végzi, hogy a feladat JSON-formátumú teszteseteit a kurzus nyelvén írt tesztekké alakítja.

Előnyök

A tesztgenerátor néhány előnye:

  1. A feladatok gyorsabban adhatók hozzá
  2. Automatizálja a feladat hozzáadásának „unalmas” részeit
  3. Könnyű a teszteket a legfrissebb kanonikus adatokkal szinkronban tartani

Használati esetek

Általában azért futtatunk egy tesztgenerátort, hogy:

  1. Előállítsuk egy új feladat tesztjeit
  2. Frissítsük egy meglévő feladat tesztjeit

Tesztek előállítása új feladathoz

Ha új feladathoz készítünk tesztgenerátort, azzal előállíthatók a feladat tesztfájlja(i). Feltéve, hogy maga a tesztgenerátor már elkészült, az új feladat tesztjeinek előállítása (sokkal) kevesebb munkával jár, mint azok nulláról való megírása.

Meglévő feladat tesztjeinek frissítése

Ha egy feladathoz már van tesztgenerátor, újrafuttathatod, hogy frissítsd, illetve szinkronizáld a feladatot a legfrissebb kanonikus adataival. Javasoljuk, hogy ezt rendszeresen tedd meg: így ellenőrizheted, vannak-e olyan problémás tesztesetek, amelyeket frissíteni kell, vagy olyan új tesztek, amelyeket be szeretnél venni.

Kiindulópont

Kétféle kiindulópont lehetséges, amikor egy feladathoz tesztgenerátort készítesz:

  1. A feladat új, így még nincsenek tesztjei
  2. A feladat már létezik, így vannak meglévő tesztjei
Caution

Ha már vannak meglévő tesztek, úgy készítsd el a tesztgenerátort, hogy az általa előállított tesztek ne törjék el a meglévő megoldásokat.

Tervezés

Általánosságban a tesztfájlok kétféleképpen állíthatók elő:

  • Kóddal: a tesztfájlok (nagyrészt) kódból állnak elő
  • Sablonokkal: a tesztfájlok (nagyrészt) sablonok segítségével állnak elő

Tapasztalatunk szerint a kódalapú megközelítés meglehetősen összetett tesztgenerátor-kódhoz vezet, míg a sablonalapú egyszerűbb.

A következő folyamatot javasoljuk:

  1. Olvasd be a feladat kanonikus adatait
  2. Zárd ki azokat a teszteseteket, amelyek a feladat tests.toml fájljában include = false jelöléssel szerepelnek
  3. Alakítsd át a feladat kanonikus adatait a sablonban használható formátumba
  4. Add át a feladat kanonikus adatait egy feladatspecifikus sablonnak

A felállás legfontosabb előnye, hogy minden feladatnak saját sablonja van, ami:

  • Nyilvánvalóvá teszi, hogyan állnak elő a tesztfájlok
  • Megkönnyíti a hibakeresésüket
  • Biztonságossá teszi a szerkesztésüket, anélkül hogy elrontanál vele egy másik feladatot
Caution

Amikor a tesztgenerátort tervezed, törekedj arra, hogy:

  • A lehető legkevesebb előfeldolgozást végezd a kanonikus adatokon a tesztgenerátoron belül
  • Csökkentsd a sablonok közötti csatolást

Megvalósítás

A tesztgenerátort általában (nagyrészt) a kurzus nyelvén írják.

Caution

Bár szabadon használhatsz más nyelveket is, minden további nyelv megnehezíti a kurzus karbantartását és a hozzá való közreműködést. Ezért ahol csak lehet, a kurzus nyelvét javasoljuk használni, mert így könnyebb a karbantartás és a közreműködés.

Formázás

Ha a kurzusodban van kódformázó tooling, érdemes ezt utófeldolgozási lépésként futtatni, miután renderelted a sablont.

Kanonikus adatok

A tesztgenerátor alapvető adatforrása egy feladat canonical-data.json fájlja. Ezt a fájlt az exercism/problem-specifications repo definiálja, amely számos Exercism-feladat közös metaadatait tartalmazza.

Caution

Nem minden feladatnak van canonical-data.json fájlja! Ha nincs, a teszteket manuálisan kell létrehoznod, mert a tesztgenerátornak nem lesz mivel dolgoznia.

Szerkezet

A kanonikus adatok egy JSON-objektumban vannak definiálva. Ez az objektum tartalmaz egy "cases" mezőt, amely a teszteseteket tartalmazza. Ezek a tesztesetek (általában) egy az egyben megfelelnek a kurzusod tesztjeinek.

Minden tesztesetnek több tulajdonsága van, amelyek közül a leírás, a property, a bemeneti érték(ek) és az elvárt érték a legfontosabb. Íme egy (részleges) példa a leap feladat canonical-data.json fájljára:

{
  "exercise": "leap",
  "cases": [
    {
      "uuid": "6466b30d-519c-438e-935d-388224ab5223",
      "description": "year not divisible by 4 in common year",
      "property": "leapYear",
      "input": {
        "year": 2015
      },
      "expected": false
    },
    {
      "uuid": "4fe9b84c-8e65-489e-970b-856d60b8b78e",
      "description": "year divisible by 4, not divisible by 100 in leap year",
      "property": "leapYear",
      "input": {
        "year": 1996
      },
      "expected": true
    }
  ]
}

A tesztgenerátor fő feladata, hogy ezt a JSON-adatot kurzusspecifikus tesztekké alakítsa. Így fordulhat le a fenti JSON Nim-tesztkóddá:

import unittest
import leap

suite "Leap":
  test "year not divisible by 4 in common year":
    check isLeapYear(2015) == false

  test "year divisible by 4, not divisible by 100 in leap year":
    check isLeapYear(1996) == true

A canonical-data.json fájl szerkezete jól dokumentált, és JSON-séma definíció is tartozik hozzá.

Beágyazás

Egyes feladatok beágyazást használnak a kanonikus adataikban. Ez azt jelenti, hogy egy cases tömb minden eleme a következők egyike lehet:

  1. Egy szabályos teszteset (gyermek tesztesetek nélkül)
  2. Tesztesetek csoportja (egy vagy több gyermek teszteset)
Note

Egy elem típusát arról ismerheted fel, hogy megvizsgálod, jelen vannak-e az adott elemtípusra jellemző mezők. Erre valószínűleg a "cases" kulcs a legjobb mód, mert ez csak a tesztesetcsoportokban fordul elő.

Íme egy példa beágyazott tesztesetekre:

{
  "cases": [
    {
      "uuid": "e9c93a78-c536-4750-a336-94583d23fafa",
      "description": "data is retained",
      "property": "data",
      "input": {
        "treeData": ["4"]
      },
      "expected": {
        "data": "4",
        "left": null,
        "right": null
      }
    },
    {
      "description": "insert data at proper node",
      "cases": [
        {
          "uuid": "7a95c9e8-69f6-476a-b0c4-4170cb3f7c91",
          "description": "smaller number at left node",
          "property": "data",
          "input": {
            "treeData": ["4", "2"]
          },
          "expected": {
            "data": "4",
            "left": {
              "data": "2",
              "left": null,
              "right": null
            },
            "right": null
          }
        }
      ]
    }
  ]
}
Caution

Ha a kurzusod nem támogatja a tesztek csoportosítását, akkor:

  • Be kell járnod a cases hierarchiát, illetve ki kell laposítanod, hogy csak a legbelső (levél) tesztesetek maradjanak
  • A teszteset leírását össze kell fűznöd a szülő leírásaival, hogy egyedi tesztnevet kapj

Bemeneti és elvárt értékek

A tesztesetek input és expected kulcsainak tartalma nagyon változatos. A legtöbb esetben skaláris értékek (például számok, Boolean-ok vagy stringek) vagy egyszerű objektumok. Előfordulhat azonban, hogy összetettebb értékekkel találkozol, amelyek valószínűleg némi előfeldolgozást igényelnek: ilyenek például a pszeudokódban megadott lambdák, a tanuló kódján végrehajtandó műveletek listái és hasonlók.

Forgatókönyvek

A teszteseteknek van egy opcionális scenarios mezőjük. A tesztgenerátor ezt a mezőt arra használhatja, hogy bizonyos teszteseteket külön kezeljen. A leggyakoribb eset bizonyos teszttípusok figyelmen kívül hagyása, például a "unicode" forgatókönyvvel jelölt teszteké, mert elképzelhető, hogy a kurzusod nyelve nem támogatja a Unicode-ot.

A forgatókönyvek teljes listája itt található.

A canonical-data.json fájlok beolvasása

Néhány lehetőség kínálkozik a canonical-data.json fájlok beolvasására:

  1. Közvetlenül lekéred őket a problem-specifications repóból (pl. https://raw.githubusercontent.com/exercism/problem-specifications/main/exercises/leap/canonical-data.json).
  2. A problem-specifications repót Git-almodulként hozzáadod a kurzus repójához.
  3. Beolvasod őket a configlet gyorsítótárából. A hely a felhasználó rendszerétől függ, de a configlet info -o -v d | head -1 | cut -d " " -f 5 paranccsal programozottan is megkaphatod a helyét.

Kurzusspecifikus tesztesetek

Ha a kurzusod szeretne néhány további, kurzusspecifikus tesztesetet hozzáadni (amelyek nem szerepelnek a kanonikus adatokban), az egyik lehetőség egy additional-test-cases.json fájl létrehozása, amelyet a tesztgenerátor összefűzhet a canonical-data.json fájllal, mielőtt renderelésre átadná a sablonnak.

Sablonok

A használandó sablonmotor valószínűleg kurzusspecifikus. Ideális esetben a sablonjaid a lehető legegyszerűbbek legyenek, szóval ne törődj a kóddublázással meg hasonlókkal.

Maguk a sablonok a tesztgenerátortól kapják az adataikat, és a generátor ezeken végigiterálva rendereli ki a sablonokat.

Note

Hogy a sablonok egyszerűek maradjanak, hasznos lehet egy kis előfeldolgozást végezni a tesztgenerátor oldalán, vagy definiálni néhány „szűrőt”, illetve bármilyen bővítési mechanizmust, amit a sablonjaid támogatnak.

A configlet használata

A configlet az elsődleges kurzuskarbantartó eszköz, amellyel többek között:

  • Létrehozhatod egy új feladat fájljait: futtasd a bin/configlet create --practice-exercise <slug> parancsot
  • Szinkronizálhatod egy meglévő feladat tests.toml fájlját: futtasd a bin/configlet sync --tests --update --exercise <slug> parancsot
  • Lemásolhatod a feladat kanonikus adatait a lemezre (ez mindkét fenti parancs mellékhatása)

Így a configlet kiválóan kombinálható a tesztgenerátorral, és igazán hatékony munkafolyamatokat tesz lehetővé.

Parancssori felület

Szeretnéd, ha a tesztgenerátor használata egyszerre lenne könnyű és hatékony. Ehhez egy vagy több szkriptfájl létrehozását javasoljuk.

Note

Szabadon választhatsz olyan szkriptfájl-formátumot, amelyik a legjobban illik a kurzusodhoz. A shell-szkriptek és a PowerShell-szkriptek gyakori, jól működő választások.

Íme egy példa egy shell-szkriptre, amely a configlet és egy tesztgenerátor kombinálásával gyorsan felállítja egy új feladat vázát:

bin/fetch-configlet
bin/configlet create --practice-exercise <slug>
path/to/test-generator <slug>

Építés a nulláról

Mielőtt elkezdenéd a tesztgenerátor építését, érdemes megnézned néhány létező tesztgenerátort, hogy érezd, más kurzusok hogyan valósították meg őket:

Ha kérdéseid vannak, azokra a fórum a legjobb hely, ahol választ kaphatsz. A Rust és a JavaScript tesztgenerátorairól szóló fórumos beszélgetések szintén hasznosak lehetnek.

Minimum életképes termék

Javasoljuk, hogy a tesztgenerátort fokozatosan építsd fel, egy minimális életképes termékkel kezdve. Egy legegyszerűbb változat beolvassa egy feladat canonical-data.json fájlját, és egyszerűen átadja az adatokat a sablonnak.

Kezdd egyetlen feladattal, lehetőleg egy egyszerűvel, mint például a leap. Csak amikor az már működik, akkor adj hozzá fokozatosan további feladatokat.

És próbáld a tesztgenerátort a lehető legegyszerűbben tartani.

Note

Ideális esetben egy közreműködő csak beilleszt vagy módosít egy meglévő sablont anélkül, hogy meg kellene értenie, hogyan működik belülről a tesztgenerátor.

Használat és közreműködés

A tesztgenerátor használata és a hozzá való közreműködés kurzusspecifikus. Keress útmutatót a kurzus README.md, CONTRIBUTING.md fájljában vagy a tesztgenerátor kódjának könyvtárában.