Cypress - snadné testování webových aplikací
Mezi nejznámější nástroje pro testování React (a obecně javascriptových) aplikací patří Jest, Mocha, Chai... A u nás v Ackee používáme pro unit a integrační testy právě Jest. Co se týče testování složitějších funkcionalit, není práce s Jestem vždy jednoduchá. Integrování více částí aplikace do testů je spojené se složitým nastavováním testovacího prostředí, mockováním a ošetřením asynchronních funkcí. Proto jsem se rozhodl vyzkoušet testovací nástroj Cypress, o kterém se dozvěděl kolega Lukáš na Reactive meetupu v Pardubicích, a v tomto článku se chci podělit o své zkušenosti.
Představujeme Cypress
Cypress je nástroj pro testování webových aplikací v reálném prostředí prohlížeče, který zjednodušuje psaní i “setup” e2e/integračních testů. Tedy těch, které testují kompletní části aplikace, ne pouze samostatné funkce/komponenty. Mimo jiné nabízí stubování (mockování) funkcí a network requestů, assertování pouhým nalezením HTML elementu, built-in ošetření asynchronní povahy webových aplikací nebo zakomponování testů do Continues Integration.
Nástroj je možné použít také pro unit testy, není to ale primární záměr a běh testů bude pomalejší, než např. v Jestu. Pro unit testy React aplikací existuje plugin.
Jedna z věcí, co je na Cypressu opravdu promakaná je UI testovacího prostředí a debugování testů. V UI testovacího prostředí vidíte vše, co se v průběhu testu v aplikaci děje a můžete se přepnout do jakéhokoliv okamžiku proběhlého testu. K tomu všemu máte k dispozici spoustu debugovacích informací, takže najít chybu v testu je snadné. Není potřeba něco luštit mezi řádky v konzoli a být nešťastný z toho, že test nefunguje.
Cypress je samozřejmě možné spustit i v konzoli, kde běží v Electron prohlížeči. Jak to všechno funguje je moc pěkně napsané v jejich dokumentaci.
Ukázka
Jak Cypress funguje, a jak se v něm píšou testy, demonstruji na jednoduchém příkladu webového formuláře pro přidávání receptů, přičemž využiju veřejné Ackee cookbook API.
Testovaný formulář
Formulář bude pro zjednodušení obsahovat 3 pole - název receptu, doba přípravy a popis. Po kliknutí na tlačítko "Přidat" se odešle POST
request na API. Pokud je recept úspěšně přidán, pod formulářem se zobrazí hláška o úspěchu, v opačeném případě o neúspěchu.
Instalace
Pokud ve svém projektu používáte NPM nebo YARN, je instalace opravdu jednoduchá:
- Balíček nainstalujeme jako dev dependency:
npm i -D cypress
. - Do souboru
package.json
přidáme do sekcescripts
příkaz pro spuštění:"cypress:open": "cypress open"
.
Nástroj se pak spustí právě přidaným npm scriptem npm run cypress:open
, otevře se okno aplikace se seznamem ukázkových testů. V projektu se vygenerovala složka cypress
s několika podsložkami, jejichž struktura a účel je popsána v dokumentaci, kde také najdete další způsoby instalace a podrobný návod. Testy se nachází ve složce integrations
.
Píšeme test
Testovací scénář našeho formuláře vypadá takto:
- Přijdeme na stránku s formulářem.
- Vyplníme pole hodnotami - název, doba trvání a popis.
- Klikneme na tlačítko Přidat.
- Zkontrolujeme, jestli se provedl dotaz na API a vrátíme simulovanou odpověď (pomocí stubu).
- Zkontrolujeme zobrazení hlášky o úspěchu.
Před samotným kódem testu je potřeba do souboru cypress.json
přidat URL adresu, na které web běž í, čímž zajistíme relativní adresaci v rámic Cypressu. V našem případě tedy localhost:
{
"baseUrl": "http://localhost:3000"
}
Vše je připraveno a můžeme jít napsat náš test, který bude vypadat skoro stejně jako testovací scénář díky jednoduchosti nástroje.
https://gist.github.com/jstorm31/3553c49d0aa6721b2ed8c7803ff87362
Nejdříve si před každým testem spustíme cy.server()
a potom cy.route()
. Tyto dva příkazy kontrolují chování network requestů a umožnují jejich "stubování". Pomocí cy.route({...})
řekneme, ať se na POST
request na definovanou URL reaguje odpovědí {}
s výchozím status kódem 200. Naši route jsme přidali alias postAddRecipe
, abychom později po odeslání formuláře mohli pomocí cy.wait('@postAddRecipe')
počkat na provedení a dokončení dotazu. Tohle je hodně užitečná funkcionalita, která umožňuje dělat testy nezávislé na externím API a definovat si všechny scénáře odpovědi včetně těch neúspěšných (404, 500,..).
Zbytek testovacího kódu už je jednoduchý. Nejdříve navštívíme požadovanou stránku, potom vyplníme hodnoty do formuláře, ten odešleme, počkáme na odpověď a zkontrolujeme zobrazení hlášky o úspěchu.
Tady jen upozorním na to, že ač se kód testu zdá synchronní, tak není. Cypress si interně přidává příkazy do fronty a může je v případě neúspěchu spouštět klidně několikrát za sebou s definovaným timeoutem. Proto si výsledek příkazu nemůžeme přiřadit do proměnné const button = cy.get('button')
a později jej použít, ale musíme použít právě alias cy.get('button').as('myButton')
. Více o aliasech a jak to vlastně pod pokličkou funguje najdete v sekci Core Concepts z dokumentace.
Spuštění
A jak vlastně test probíhá?
Otevře se okno prohlížeče ve kterém se přehraje celý test. V levém sloupci vidíme všechny operace, které se provedly a kliknutím na ně se můžeme vráti do daného okamžiku k prozkoumání. Debugování testů je pak opravdu snadné. Do konzole prohlížeče se dokonce přidávájí další podrobné informace, což je užitečné hlavně u API requestů.
Problém s Fetch API
Cypress zatím bohužel nepodporuje Fetch API, které v mnohých moderních webových aplikacích nahrazuje XHR, jak píšou na stránce known issues. Pokud jej ve svých projektech používáte, nebude vám fungovat stubování network requestů. Pro tuto chybějící funkcionalitu existuje dočasné řešení, které spočívá v nahrazení Fetch API polyfilem. Blíže se tímto problémem komunita zabývá na GitHubu projektu.
Závěrem
Cypress oproti existujícím řešením přináší jednoduchý a rychlý způsob, jak automaticky testovat komplexnější flow webových aplikací, a tím nám ušetří práci se stále stejným manuálním testováním. Díky elegantnímu řešení asynchronních požadavků se v něm dá psát kód, jako by byl synchronní. Dovoluje nám testovat různé odpovědi z externích API, a tím silně prověřit funkcionalitu od začátku do konce.
Na unit testování (nejenom komponent, ale třeba i selektorů) se Cypress tolik nehodí a je lepší použít Jest nebo podobné nástroje, u kterých bude mimo jiné rychlejší běh testů.
O dalších tipech pro Cypress se můžete dočíst na našem frontend cookbooku.