časť 13
Ako testovať hru, ktorú nikdy neprestaneme meniť
Vyše tisíc dvesto testov, mutanty, ktoré sa musia dať zachytiť, a chyba, ktorú našiel hráč, nie test. Čo testovanie hry naozaj stráži a kde zlyháva.
V roku 1993 sa pred nahraním hry Doom na server podľa spomienok testovalo 30 hodín v kuse. Dnes máme na Audamire 1 234 testov, ktoré prejdú za jednu až tri minúty. Správny pocit z takého čísla je skôr nedôvera než pokoj: ak je testov veľa, prečo sa stále nájde chyba?
Tento článok je o tom, čo sme pri testovaní hry zistili. Číslo testov nie je dôležité. Dôležité je, ktoré druhy ochrany držia hru pokope, a kde sa ukázalo, že zelená farba nie je dôkaz.
Takto rástol počet testov po dňoch od 17. októbra:
test( v súboroch s testami na poslednom zázname dňa, teda približný počet: časť testov vzniká v cykloch, takže v strome z 6. 11. je 4 256 volaní a celý beh node tools/verify.js napočítal 4 564 testov. Git história hry začína 18. 10.; stĺpec z 17. 10. je podľa uloženej kópie projektu z 22:57 a 25. 10. sa nezapisovalo. V čase článku (20. 10.) bolo podľa node --test 1 234 testov.Čo stráži hru
Ochrana hry sa skladala postupne, vždy potom, ako sa ukázalo, čo chýba. Dnes sa dá zhrnúť do šiestich vrstiev:
- Logika bez obrazovky. Pravidlá, AI, hlavolamy, kampaň a ekonomika sú čisté moduly bez DOM (rovnaký kód beží v prehliadači, vo Workeri aj v
Node). Dajú sa testovať rýchlo a deterministicky. - Obrazovky proti falošnému DOM. Skutočné skripty používateľského rozhrania bežia v
Nodeprotitests/fake-dom.js. Rýchle, ale falošný DOM nepozná CSS. - Odtlačky. Zlatý odtlačok mapy z pevného seedu, odtlačok 172 bežných partií (
baseline), 18 kontrolných meraní (calibrate --verify), záznamy partií, ktoré sa musia prehrať rovnako. - Merania. Každý nástroj má
--check: misie, minihry, hlavolamy, ekonomika kociek, úspechy. Hovoria, či sa hra nezmenila v obtiažnosti, nielen v správnosti. - Smoke test hotového produktu. Skutočný prehliadač (headless Chromium) hrá skutočnú zostavu so skutočnými kliknutiami a klávesmi.
- Nezávislá revízia. Po každej väčšej fáze číta kód iný agent, ktorý nič nemení a len hlási.
Tých istých šesť vrstiev na jednom obrázku, každá s jedným číslom z hry zo 6. novembra (v texte sú čísla z 20. októbra):
ce303e2, jeden celý beh node tools/verify.js: 32 min 5 s, všetky fázy prešli), kým text má čísla z 20. 10. Prvá a druhá vrstva majú číslo spoločné, lebo obe žijú v jednej sade testov. Číslo pri štvrtej vrstve je počet nástrojov hry, ktoré čítajú voľbu --check (najmenej 36, preto „35+“), číslo pri šiestej je z textu článku.Z toho je zaujímavá práve piata vrstva a práve tá šiesta, lebo bez nich by prvé štyri tvrdili len to, čo sme od nich čakali.
Mutácie: testovanie testov
Test, ktorý nikdy nezlyhá, nemusí byť dobrý. Môže byť len slepý. Preto sa po každej fáze robí mutačná kontrola: do kópie kódu sa zámerne vložia chyby (jedna zmena naraz: obrátená podmienka, iný limit, vynechaný krok) a pustí sa sada testov. Ak ju zmena nepoloží, test chýba.
Jedna mutácia vyzerá takto (skutočný riadok hry a skutočný test z dnešného stavu):
js/core/feedback.js, funkcia, ktorá skracuje text hráča na limit; dev, verzia 0.119.0). Zmena <= na < bola spustená 6. 11. 2026 na kópii súboru so skutočným testom tests/core/feedback.test.js: pôvodný kód prejde všetkých 39 testov, s mutantom jeden padne. Je to mutácia rovnakého druhu ako v kontrole z 20. 10., nie jedna z jej 126 (tie sa nezachovali).Skutočné čísla z tohto projektu:
- Okno „Nahlásiť chybu alebo nápad“: 126 mutácií (každý limit, čistenie textu, adresy v chybách, odkazy, záložné postupy kopírovania, zaostrenie, Esc a Tab). Po prvých kolách ich test nezachytil 8. Každá prežitá mutácia viedla k novému testu. Nakoniec sa zachytili všetky.
- Generátor území: 37 mutantov, 35 zachytených, tri z nich až po rozšírení testov o zlatý odtlačok. Dva prežili preto, že išlo o nadbytočný kód. Z projektu sa odstránil.
- Podľa záznamov z vývoja (tieto počty nie sú v dokumentácii projektu): železný beh približne 55 mutantov, Puzzle Labs približne 66, Zbierka 27 a 88, rebrík Hrozby okolo 175. Všetko sa zachytilo po doplnení testov, okrem jedného ekvivalentného mutanta.
Prvé dve kontroly spolu s dnešnou treťou (premenovanie spoločníkov v kampane, 6. novembra) na jednom obrázku:
docs/DESIGN.md §22 a §19.6), tretia z 6. 11. 2026 (27 cielených mutácií pri premenovaní spoločníkov v kampane). Údaj o prvom kole generátora území (32 zachytených, 5 prežilo) je dopočítaný z vety, že z 35 zachytených mutantov sa tri zachytili až po rozšírení testov. Prežitý mutant je chýbajúci test alebo zbytočný kód.Dôležitá veta, ktorá sa dá vyčítať z týchto čísel: prežitý mutant je buď chýbajúci test, alebo zbytočný kód. Oboje sa oplatí vedieť.
Rovnaký princíp sa uplatňuje aj pri smoke teste hotovej zostavy. Overilo sa, že zlyhá, keď má zlyhať: 18 zámerne pokazených zostáv (chýbajúci odkaz, Esc, ktoré nezatvorí okno, kopírovanie, ktoré tvrdí úspech a nič nekopíruje, odkazy von v zostave pre portály, chýbajúci service worker, pokazená cesta k Workeru, chyba v konzole pri štarte a ďalšie). Všetkých 18 chytil smoke test a každú z dobrého dôvodu. Hlásič požiaru sa neskúša tým, že sa naň pozerá, ale tým, že sa zapáli zápalka.
Tri pasce, na ktoré sa narazilo
1. Test, ktorý nikdy nezlyhá
Pomocná funkcia waitFor čaká, kým sa splní podmienka. Pri vypršaní času **len vráti false** a nevyhodí výnimku. Test, ktorý ju zavolá bez assert.ok, preto prejde aj vtedy, keď sa koncová karta nikdy neukáže. Odhalila to až mutácia. Pravidlo odvtedy: podmienka sa vždy zapisuje assert.ok(await waitFor(…)).
Podobná pasca je v samotnom Node. Zrušený test (vypršal časový limit) hlási fail 0 a cancelled 1, ale proces skončí nenulovým kódom. Mutačný skript, ktorý sa pozeral na počet zlyhaní, vyhlasoval také mutanty za „prežité“. Rozhoduje návratový kód procesu.
2. Test, ktorý zrúti stroj
Pri mutačnej kontrole jeden skúšobný proces narástol na približne 33 GB pamäte a stroj sa zasekol. Dvakrát, lebo po prvom páde sa to isté spustilo znova, skôr než sa našla príčina.
Príčina: padajúci assert.equal(prvok, null), kde prvok bol prvok falošného DOM. Node pri zlyhaní vypíše a porovná oba operandy a graf jedného prvku je celý falošný dokument. Test, ktorý mal oznámiť „toto nesedí“, sa pokúsil vypísať celý svet.
Tri pravidlá odvtedy platia:
- **Do
assert.equalsa nikdy nedáva prvok DOM.** Porovnáva sa identita (assert.ok(a === b, 'popis')) a hlavičkafake-dom.jsna to upozorňuje. - Ťažké behy sa spúšťajú po jednom procese a v malých dávkach, s limitom pamäte (
ulimit -v). - Po páde sa najprv hľadá príčina. Nespúšťa sa to isté znova.
Posledné pravidlo je z celej série asi najdrahšie získané a najpoužiteľnejšie mimo hier.
3. Chyba, ktorú našiel človek
Obrazovka Puzzle Labs sa nedala posúvať, takže časť zoznamu ostala mimo dosahu. V CSS chýbalo overflow-y: auto. Testy túto chybu nemohli nájsť, lebo falošný DOM CSS nepozná. A ručná kontrola ju prehliadla: snímka obrazovky „zoznam dole“ vyšla presne ako „hore“ a nikto si to nevšimol.
Z toho vznikli tri veci:
- test
layout.test.js, ktorý staticky overí, že každá obrazovka okrem hry máoverflow-y: auto, - jednorazové skripty, ktoré posúvajú skutočným kolieskom a dotykom (cez protokol DevTools) a porovnajú pozíciu pred a po (v repozitári ostal len statický test),
- pravidlo: snímky „pred a po posunutí“ musia vyjsť rôzne; ak sú rovnaké, je to chyba, nie náhoda.
Nezávislá revízia aj sto testov môžu mlčať a človek s myšou to nájde hneď. Preto sa overenie hotovej zostavy robí aj skutočným vstupom: klikom, kolieskom, klávesom a dotykom na mobile.
Keď testovacie dáta klamú: migrácia
Hra ukladá profil, kampaň, zálohu a rozohrané partie do prehliadača. Keď sa zmenia ID (v tomto projekte sa menili slovenské identifikátory na anglické), treba zaručiť, že staré uloženie ešte funguje a nič sa nevyplatí dvakrát.
Test s ručne napísanými údajmi prešiel. Reálny profil však odhalil chybu: štyri úspechy Puzzle Labs sa po premenovaní laboratórií vyplatili druhýkrát, lebo ID úspechu sa odvodzuje z ID laboratória a v tabuľke migrácie chýbalo. Zistilo sa to tak, že sa starým kódom (z posledného commitu pred zmenou, cez git archive) vytvoril profil, kampaň, záloha aj uložená hra a načítali sa novým kódom. Nezávislá revízia našla ten istý problém až po zhruba dvadsiatich minútach, porovnanie dvoch množín ID ho nájde za sekundu.
Lekcia: testovacie dáta písané rukou odrážajú to, čo autor chce, nie to, čo existuje. Ak sa mení formát, ktorý hráč už má uložený, dáta musí vyrobiť starý kód. Kto to nerobí, objaví chybu až u prvého používateľa.
Rovnaký princíp stojí za dvomi nástrojmi (talents-migration.js, integration-check.js), ktoré z archívu starého stromu vyrobia profily vo viacerých štádiách a overujú, že ich nový kód zvláda.
Determinizmus ako superschopnosť
Najlepšia vec, ktorú hra pre testovanie urobila, nie je žiadny test. Je to, že celá náhoda hry sa odvodzuje zo stavu (seed, kolo, ťah, poradie útoku). Preto je každá partia reprodukovateľná:
tools/replay.js --games 300 --checkzahrá 300 náhodných partií s AI, zaznamená ich a prehrá. Každý kontrolný bod aj výsledok sa musia zhodovať.- Uložená hra po návrate nehodí kocky znova (nedá sa „zopakovať“ hod odchodom na domovskú obrazovku).
- Chybové hlásenie nesie seed a „soľ“ hodov, takže chybu je možné zopakovať.
Čas je ďalší zdroj náhody. Testy denného hlavolamu mali pripnuté hodiny stránky na „ľahký deň“, lebo obtiažnosť rastie počas týždňa a sada testov by inak závisela od toho, v ktorý deň sa spustí (overilo sa nastavením hodín na najťažšiu nedeľu). V testoch s Workerom ostal visieť dvojminútový časovač, ktorý držal proces Node nažive.
Revízie agenta: čo vedia a čo nie
Po každej väčšej fáze (Puzzle Labs, Zbierka, Avatar[1] H1 až H4, záznam hry, úspechy, analýza) číta kód iný agent a len hlási. Je to lacná forma druhého páru očí. Podľa záznamov z vývoja (nie všetky čísla sú v dokumentácii): pri kampani našla revízia 7 reálnych chýb, pri Zbierke 16 nálezov (7 stredných a 9 nízkych), pri úspechoch 12, pri analýze 13 a pri Puzzle Labs žiadnu vážnu, ale niekoľko skutočných (nápoveda kreslila šance aj v hlavolame, koncová karta sa mohla zrútiť pri plnom zozname výsledkov).
Čo revízia nenašla, je rovnako poučné: neprišla na to, že sa obrazovka Puzzle Labs nedá posúvať. Zato našla okrajové prípady: dve karty prehliadača, ktoré prepisujú jeden stav, „mŕtvy“ beh po zahodení posledného pokusu, zlyhanie kopírovania. Je to dobrý doplnok. Nie je to náhrada za človeka, ktorý hru skutočne hrá.
Čo si z toho odnášam
- Zelená farba je tvrdenie, nie dôkaz. Test, ktorý nikdy nezlyhal, treba pokaziť, aby sa dokázalo, že vie zlyhať.
- Prežitý mutant je dar. Buď chýba test, alebo je v kóde nepotrebná vec.
- Testy vyrobené z hlavy klamú. Ak sa mení uložený formát, vytvor dáta starým kódom.
- Hraj skutočným vstupom. Koliesko, dotyk, kláves. Kto neposúva, nevie, či sa to posúva.
- Po páde nepúšťaj to isté znova. Najprv príčina.
- Rýchlosť je vlastnosť. Minúta alebo dve sa dajú spúšťať po každej zmene. Hodina by sa spúšťala len pred odovzdaním.
K poslednému bodu: takto vyzerá jeden príkaz, ktorý dnes overí celú hru:
node tools/verify.js, ktorý jedným behom overí celú hru: strom hry ce303e2 zo 6. 11. 2026, 32 min 5 s, všetky fázy prešli. Popisy fáz sú vynechané a dlhé riadky skrátené znakom „…“, výpis ostáva anglický, ako ho píše hra.A kam sa pri ňom míňa čas:
Zdroje a poznámky
- Počty:
node --testv hlavnom strome hry z 20. 10. 2026 (1 234 testov, 1 233 prešlo, 1 preskočený, 82 súborov s testami,--test-concurrency=3: 161 s). Na kópii projektu z predchádzajúceho rána (1 187 testov) trvala sada s predvoleným paralelizmom 61 s. Dĺžka testov približne 29 000 riadkov. - Mutácie a smoke test zostavy:
docs/DESIGN.md§22 (126 mutácií, 18 pokazených zostáv), §19.6 (37 mutantov); zvyšok z poznámok k fázam. - Pamäťová nehoda:
docs/DESIGN.md§22.3 („A lesson about tests and memory“) a záznam v histórii (commit z 18. 10. 2026, 23:24). - Obrázky (orientačné, čísla sú dnešné, nie z 20. 10.): čísla vrstiev a výpis sú z jedného celého behu
node tools/verify.jsna strome hryce303e2zo 6. 11. 2026 (dev krátko pred verziou 0.119.0; 32 min 5 s, všetky fázy prešli). Rast počtu testov je počet volanítest(v súborochtests/**/*.test.jsna poslednom commite každého dňa vetvydev(približne; 17. 10. z uloženej kópie projektu). Mutácie sú z textu článku a z kontroly premenovania spoločníkov v kampane (27 mutácií, 6. 11. 2026). Riadok kódu a test v nákrese mutácie sú skutočné (js/core/feedback.jsatests/core/feedback.test.jsna dev 0.119.0, spustené 6. 11. 2026). - Doom a 30 hodín testovania: výskum k úvodnému článku (Kushner, Masters of Doom, cez Wikipédiu); ide o spomienku, nie o meranie.
Poznámky
- Do verzie 0.80.1 z 1. novembra 2026 sa Avatar volal Hero Dice (pozri Z Hero Dice na Avatar (pripravovaný článok č. 39)). ↩︎