Na posledních Ackee meets jsem mluvil o nativním Node.js testovacím test runneru a modulu fetch, funkci na provádění HTTP požadavků, která slibuje kompatibilitu s Web API. Znamená to tak, že Jest, Mocha a další už nebudou potřeba, nebo například místo axios a got začneme používat fetch? Na to odpověděl můj talk, pro čtenáře to shrnu tady a teď.
Test runner se poprvé dostal k náhledu v Q4 2020 jako experimentální pro LTS verze 16 a 18. O několik let později v Q4 2023 se stal stabilní s verzí 20.0.0, tehdy ovšem bez podpory JUnit formátu, který jsme v té době používali. Brzy jsme se dočkali aktualizace a ve 20.0.9 dorazil i ten.
Node:test není jen test runner. Podporuje většinu funkcionalit, na které jsme zvyklí z ostatních frameworků. Podpora skupin testů, mockování i coverage.
Proč se ale vůbec zabývat nějakým testovacím runnerem, když Node.js ekosystém už jich plno poskytuje? Jen abych uvedl pár příkladů: Jest, Mocha, Vitest, Ava.
- Závislost navíc. Závislosti je nutné aktualizovat, mnohdy držet interoperabilitu více balíčků najednou.
- Jednoduchost. Testovací frameworky obsahují plno užitečných funkcí, které ale zvyšují složitost jeho použití, a přitom by mnohdy stačily základní funkce, u kterých vývojář nemusí přemýšlet, jestli je použije správně.
- Stabilita. Pokud celé testování pokryjí nativní moduly, odpadnou škatulata s potřebou instalace několika nezávislých balíků od různých vendorů, a zároveň hlavní Node.js stabilní větev bude vždy mít v zájmu všechny uživatele Node.js.
Co ten test runner vlastně umí? Jdeme na to.
Test runner: Základní interface
Na první pohled to vypadá velmi podobně jako jste zvyklá odjinud. Describe, test, it – standardní testovací rozhraní.
Assertion modul je v Node.js je delší dobu, proto ho představovat nebudu.
Testy je možné pustit jak přes CLI, tak programově. Přepínač --test pustí test runner nad vybranými (nebo výchozími soubory). Pokud máte codebase v TypeScriptu, je to možné přes require modulu ts-node. Stejně jako je to možné u ostatních frameworků nebo obyčejných spuštění Node.js aplikací.
Node:test dále podporuje
- Asynchronní testy. Použitím async funkcí pro test casy, případně callbacků.
- Skupiny testů. Pomocí describe bloků, jako je zvykem. Takto definované skupině je tak možné nadefinovat bloky, které se mají provést před/po všech testech, anebo před/po každém testu.
- Podtesty. Podtesty jsou celkem nevšední funkce, která umožňuje vytvářet testy z kontextu jiného testu. Shrnutí běhu testů pak může nejen ukázat hierarchii testů, ale zároveň nepustí vnitřní testy, pokud rodič selže. FYI Mocha, Jest ani AVA tuto funkci nemají, někdy cíleně.
Test runner: Spies
Vedle testovacího frameworku modul nabízí i funkci spies: tedy mockování a pozorování chování na objektech, funkcích a modulech. Samozřejmostí je i nahrazení chování funkcí pomocí stubs. Pokročilé metody tak dovedou mockovat/stubovat i metody objektů, reset/restore mocku a experimentálně i mockování časovačů v Node.js.
Skvělé chování ale nabízí spies v kombinaci s podtesty. Použití spies z kontextu testu zajistí, že po skončení testu se spy automaticky obnoví na původní chování. Odpadá tak nekonečné obalování nastavováním před testem a čištěním po něm.
Použití může vypadat např. takto:
Test runner: Globální setup a teardown
Samotný test runner tuto funkci neposkytuje. Je možné ji simulovat, ale pouze za předpokladu, že se vzdáte CLI rozhraní a rozhodnete se využít volat test runner programově. V takovém případě si ale musíte obstarat všechno od výběru souborů k otestování, výstupů a reportingů sami.
Fetch
Fetch je globální funkce dostupná v Node.js prostředí a kopíruje funkci fetch, kterou znáte z browser Web API. Jedná se o rozhraní pro získání dat přes HTTP.
Samotnou iniciativu i funkci velmi cením. Nabízí rychlou a standardní cestu pro tvorbu HTTP požadavků. Rychle a s minimem kódu.
Pro složitější a přesnější použití HTTP volání, např. pokud chcete využít timeoutů, nebo automatické opakování požadavků, se ale začne rozhraní komplikovat.
Timeout se volí zvláštně přes rozhraní signal, kde jedinou správnou hodnotou je instance AbortSignal. Možná se dočkáme dalších signálů v budoucnu, momentálně toto rozhraní působí dosti zmateně.
Chybovost na síti je častý jev, a bývá tak zvykem, že HTTP knihovna dovoluje dělat automatický retry podle daných nastavení. Fetch tímto zatím nedisponuje. Stejně jako neposkytuje větší kontrolu nad jednotlivými typy timeoutů, jako jsou např. pro získání DNS nebo navázání TCP spojení. Pokud jste zvyklí nějakým způsobem reagovat na události životního cyklu požadavku, při použit fetch na ně zapomeňte – takovou funkci zde nenajdete.
Další kaňku rozhraní fetch pro Node.js tvoří fakt, že pokud se rozhodnete použít ReadableStream jako tělo požadavku, musíte explicitně nastavit vlastnost duplex: half, i když jiná hodnota není v Node.js platná. Inu, ale je to kompatibilní s Web API, no.
Co si odnést
Node:test nabízí plno funkcí dostatečných pro testování Node.js aplikací bez nutnosti instalace dalších balíčků. Zatím to nemám prověřené na žádném velkém produkčním projektu, ale na menších funguje na výbornou. Zprvu se může zdát, že se mnoha věcí vzdáváte, ale nakonec zjistíte, že stejně nejsou potřeba.
Fetch, přestože chápu motivaci, má pro nás trochu užší použití. Je skvělý pro MVP, kde není potřeba si s HTTP požadavky lámat hlavu, pro pokročilejší manipulaci však stále doporučuji použít specializované knihovny, axios či got.