Abychom ověřili správnou funkcionalitu jednotlivých částí systému při implementaci Knížkomatu, bylo nezbytné náš kód otestovat. Pro backend jsme zvolili přístup přes integrační testy, které ověřovaly správnou funkčnost jednotlivých endpointů, o nich však tento článek nebude. Na frontendu jsme se rozhodli využít end-to-end testy uživatelského rozhraní, které procházejí vybrané scénáře v rámci aplikace, jako by se jednalo o běžného uživatele. Pro tyto frontendové testy jsme měli několik základních požadavků:
Všechny tyto požadavky splňuje knihovna Cypress, jelikož umožňuje psát testy v JavaScriptu, testy lze spustit přes NodeJS a funguje velice snadno na jakémkoli zařízení. Aby testy nebyly závislé na backendu a šlo snadno kontrolovat data, se kterými test pracuje, rozhodli jsme se, že budeme požadavky odcházející z frontendu nějakým způsobem mockovat a vracet předpřipravené odpovědi. Zde jsme však narazili na hlavní problém celého našeho plánu a tím je middleware psaný v NextJS.
V hlavním článku o projektu Knížkomat jsme zmiňovali, že middleware mimojiné usnadňuje vytváření požadavků na frontendu. To je pravda, ale jelikož má middleware komunikaci s frontendem ve svojí režii, není tato komunikace tak srozumitelně definovaná, jako je komunikace mezi middleware a backendem, která se řídí RESTovým API definovaným na backendu. Pokud tedy v Cypressu chceme mockovat požadavky, je potřeba odchytávat zprávy bez předchozí znalosti toho, jak tyto požadavky vlastně vypadají. To sice není nereálný úkol, ale je rozhodně velmi náročný, jelikož je potřeba pro každý požadavek odchytávat všechny zprávy a následně ručně “luštit”, co každá zpráva znamená, v jakém je formátu a jakou očekává odpověď. To vše, když jen o krok dál (tedy mezi middleware a backendem) máme krásně nadefinovanou a lidsky čitelnou komunikaci.
Prvním řešením, které nás napadlo, bylo “zabalit” do procesu v Cypressu i middleware tak, aby pak šlo odchytávat komunikaci na backend. Jelikož je ale middleware samostatný proces, Cypress s ním nechce mít nic společného a přestože ho spustí jako součást testu, tak ho není možné obalit do mockovacích funkcí. Napadlo nás tedy několik alternativních řešení.
Jednou z variant by bylo nasadit samostatnou instanci backendu, která by sloužila pouze pro potřeby testů. Problémem tohoto přístupu je, že backend by byl perzistentní a pokud by některý test upravil data na backendu, tak při dalším spuštění testu by byla data už změněná, což porušuje zásadu konzistentnosti testů. I kdyby se konzistence dat nějak vyřešila, byl by na serveru spuštěná plnohodnotná instance backendu, která by většinu času vůbec nic nedělala, což není příliš efektivní.
Dalším možným řešením je spouštět backend v nějaké formě virtualizace, nejspíše v Dockeru. To by nám umožnilo snadno a “čistě” vytvářet a ukončovat instance backendu a nijak by to nenarušilo konzistenci testů. V rámci CICD by šlo tento přístup využít, ale Docker narušuje třetí požadavek zmíněný v úvodu, jelikož někteří z nás mají vývojové stroje na Windows, který komplikuje možnost integrovat Docker do testů. Nejedná se o nepřekonatelný problém, spíše otravný.
Třetí možností bylo vytvořit minimální verzi backendu, kterou by šlo snadno spouštět jak na vývojových strojích tak při CICD. Tato minimální verze by byla vytvořená pouze pro využití v testech a mohla by být napsaná např. v Pythonu, aby byl srozumitelný a rychle spustitelný. Jedinou nevýhodou je potřeba spouštět se Cypress testy a backend samostatně.
Naše finální řešení vychází z uvědomění si, že minimální verzi backendu je možné napsat i přímo v JavaScriptu a že ho pak může spouštět Cypress v rámci konfigurace setupNodeEvents. Vytvořili jsme tedy backend na frontendu přes jeden soubor, který funguje jako klasický server, tedy poslouchá na vybraném portu a odpovídá na vybrané požadavky předdefinovanými odpověďmi. Díky tomu nám nevadí, že middleware není možné mockovat, protože celý server, na který se middleware dotazuje, je nastavený přesně pro potřeby testů. Výhodou tohoto přístupu je, že celý proces se spouští stejným přikazem jako samotné testy, takže ho lze pouštět jedním kliknutím na vývojovém stroji nebo jedním příkazem v rámci CICD.
NoxLabs je tým inženýrů a designérů, kteří se specializují na vývoj webových a mobilních aplikací. S nadšením vytváříme krásný software a vítáme nové nápady na projekty.