Testování
Bezkonkurenčně nejpoužívanější framework pro testování v Node.js je jest, který má hromadu funkcionalit a doporučuji ho použít pro větší projekty. Pro jednoduchost, my použijeme ava.
Velmi populární je také kombinace knihoven
mochaachai.
Nainstalujeme balíček ava jako development dependency (testy spouštíme pouze při vývoji, ne za běhu aplikace).
V package.json upravíme script test
Otestujeme, zda vše funguje
Jelikož nemáme napsané žádné testy, ava nemůže žádné spustit. Vytvoříme si adresář tests kde budou soubory končíci na příponu .spec.js (nebo .test.js) kam budeme jednotlivé testy psát.
specje zkratka pro specification - testy slouží jako specifikace toho jak by se program měl chovat
Jednotkové testy (unit tests)
V dnešní době se jednotkové testy těší velké popularitě. Jednotkové testy netestují celý program ale pouze jeho jednotky (malé kousky). Jednotka může být funkce, třída, nebo skupina funkcí/tříd. Jednotkové testy jsou nejvhodnější pokud testujeme kód který má vstup, výstup a žádné vedlejší efekty (komunikace s databází, webový server, manipulace se soubory, ...).
Vytvoříme si soubor s jednotkovými testy. Na názvu (kromě .spec.js přípony nezaléží) a tak vytvoříme soubor tests/unit.spec.js
Pomocí npm run test spustíme testy a ava nám vrátí informaci, že všechny testy prochází. Zkuste změnit jedno true na false (nebo jinou hodnotu) a znovu vyzkoušet npm run test.
ava nabízí několik funkcí na testování hodnot
Test driven development
Při psaní jednotkových testů je možné praktikovat TDD - test driven development. Nejprve píšeme testy a pak teprve kód. Vyzkoušíme si to na funkci FizzBuzz.
Máme napsaný test a pomocí npm run test můžeme vyzkoušet zda naše implementace FizzBuzz je dostatečná.
Jak můžeme vidět, funkce fizzbuzz měla vrátit 1, ale vrátila undefined. To je, jednoduché napravit.
Testy prochází a můžeme napsat další test.
Spustíme testy a zjistíme, že fizzbuzz měl vrátit 2 ale vrací 1. Opět jednoduché opravit.
Testy nám opět fungují. Ale tato funkce uričtě neimplementuje FizzBuzz algoritmus. Musíme se tedy zamyslet jaký nejjednodušší a zároveň neprocházející test napsat.
Testy neprocházejí a dáme se do opravování.
Pokud se zamyslíme, tato podmínka určitě není korektní. Ale TDD nám říká, že neprocházející test máme opravit co nejjednodušším kódem (v rámci mezí), i když víme, že není 100% korektní. Dříve nebo později se ale díky dalším testům ke korektnímu kódu dostaneme. Jdeme tedy psát další test.
A opravujeme.
A další test.
Nyní použijeme už “inteligentní” opravu. Nemá cenu to dále protahovat.
A další test.
A podobná oprava jako u trojky.
A další test.
Nejprve uděláme opět hloupou opravu.
A dalším testem se jí pokusíme “zinteligentnít”:
A poslední oprava.
Nyní už nejsme schopni vymyslet žádný test který by nejprve neprocházel (a zároveň nebyl nesmyslný) a tak víme, že je funkce fizzbuzz korektní.
TDD lze aplikovat i u jiných typů testů než jsou jednotkové, ale u jednotkového testování bývá TDD nejběžnější a nejjednodušší na provedení.
Užitečné testy (integrační/regresní/akceptační)
I přesto, že jednotkové testování je super, často se stává, že není úplně použitelné. Jak by jsme například pomocí jednotkových testů testovali naši ToDo aplikaci? Rozdělená do funkcí moc není a i kdyby byla tak funkce budou tak jednoduché, že je pomalu nemá cenu testovat. Většinu věcí co naše aplikace dělá jsou vedlejší efekty (komunikace s databází, servírování HTML přes express, ...) což není pro jednotkové testy vhodné.
Testy by vždycky měli testovat veřejné rozhraní čehokoliv (funkce, třídy, aplikace). Co je veřejné rozhraní naší ToDos aplikace? Na vstupu HTTP požadavky a HTML jako odpověď. Pojďme tedy naši aplikaci otestovat jako celek. K tomu nám pomůže super knihovna supertest.
Aby jsme mohli testovat naši aplikaci jako celek, musíme provést jeden refaktor. Aktuálně aplikace i její spuštění (app.listen) se nachází v jednom souboru. Nemůžeme tedy pracovat s naší aplikací bez toho aby se automaticky spustil HTTP server. Extrahujeme tedy celou aplikaci do souboru src/app.js. Upravíme cesty importů, exportujeme express aplikaci app a smažeme port a kód který vytváří server.
Tento kus kódu z src/app.js smažeme:
A index.js nyní vypadá takto:
Testovací databáze
Jelikož testujeme celou aplikaci, testy budou šahat do databáze. Nechceme ale aby nám testy ovlivňovali naši databázi (nebo naopak stav databáze ovlivnil testy). Budeme potřebovat tedy databázi speciálně pro testování.
Upravíme knexfile.js
Uvnitř knexfile.js máme nyní dvě konfigurace. Jednu pro vývoj (development) a druhou pro testování (test). Jelikož chceme aby testy byli rychlé stav databáze nás přímo nezajímá, můžeme pomocí :memory: SQLite říct aby nevytvářela soubor na disku ale data ukládala pouze do paměti.
Musíme ještě upravit src/db.js a knexu předat správny objekt s konfigurací databáze (teď neví zda vybrat development nebo test).
process.env.NODE_ENV je speciální proměnná která obsahuje prostředí ve kterém Node.js běží. Pokud spustíme testy, bude obsahovat hodnotu test. Pro jistotu přidáme ještě || 'development' což způsobí, že pokud process.env.NODE_ENV je prázdný, použije se hodnota development (|| je nebo).
Nyní můžeme začít psát testy.
Testování ToDos
Vytvoříme si tests/todos.spec.js . Ještě než se spustí nějaký test musíme dostat databázi do správného stavu. Na to využijeme migrace a beforeEach a afterEach funkce které poskytuje ava.
A můžeme začít prást první test.
Nejprve musíme do databáze vložit nějaké ToDo.
Nyní uděláme dotaz na / a podíváme se, zda v HTML, které jsme dostali ze serveru najdeme text Testovací todo!!! .
Nyní můžeme vyzkoušet, že například odebráním <td><%= todo.text %></td> z views/_todos.ejs test rozbijeme.
Nejedná se sice o dokonalý test, ale 1) testujeme reálné veřejné rozhraní aplikace 2) ověřujeme, že ToDos se tak nějak vypisují do HTML, což nám ke spokojenosti stačí.
Můžeme si napsat i druhý test na vytváření ToDos.
