En snabbguide till att skriva end-to-end tester med jest-puppeteer
Den här artikeln ger en snabbguide för att skriva effektiva end-to-end tester med jest-puppeteer, med fokus på installationsprocessen, vanliga API:er och praktiska testsituationer med hjälp av en enkel att-göra-app som exempel.
Som en del av vårt engagemang för att säkerställa kvaliteten på Logto och kontinuerlig förbättring, använder vi jest-puppeteer för end-to-end automatiserad testning. Detta gör att vi snabbt kan iterera på Logtos utveckling utan störningar.
I denna artikel delar vi med oss av vår erfarenhet av att skriva effektiva jest-puppeteer testskript med en enkel att-göra-app som exempel. Vårt mål är att hjälpa dig att snabbt komma igång med att skriva jest-puppeteer testkod för dina egna projekt.
Introduktion till end-to-end testning med jest-puppeteer
End-to-end testning är ett sätt att säkerställa att din applikation fungerar korrekt ur användarens perspektiv. För att uppnå detta använder vi två viktiga verktyg: Jest och Puppeteer.
Jest är ett populärt JavaScript testningsramverk som erbjuder ett användarvänligt API för att skriva tester och assertioner. Det används flitigt för enhets- och integrationstestning.
Puppeteer är ett Node.js-bibliotek utvecklat av Chrome-teamet som tillhandahåller ett hög nivå API för att kontrollera headless Chrome eller Chromium webbläsare. Detta gör det till ett idealiskt val för att automatisera webbläsarinteraktioner i end-to-end tester.
jest-puppeteer är ett Jest-presett som möjliggör end-to-end testning med Puppeteer. Det erbjuder ett enkelt API för att starta nya webbläsarinstanser och interagera med webbsidor genom dem.
Nu när du har en grundläggande förståelse av de viktiga verktygen, låt oss dyka in i att ställa in din testmiljö.
Ställa in din testmiljö
Att ställa in din testmiljö för end-to-end testning med jest-puppeteer är en enkel process som innefattar tre huvudsteg:
- Installera beroenden
I ditt projekt (eller skapa ett nytt projekt), öppna terminalen och kör:
Gå sedan till node_modules/puppeteer
och installera Chromium (som behövs för Puppeteer):
- Konfigurera Jest
Nästa steg är att konfigurera Jest för att fungera sömlöst med Puppeteer.
Skapa en Jest-konfigurationsfil (t.ex., jest.config.js
) i ditt projekts rotkatalog om du inte redan har en. I den här konfigurationsfilen anger du jest-puppeteer som ett preset:
Du kan anpassa andra Jest-inställningar i denna fil efter behov. För mer information om hur du anpassar Jest-konfigurationer, vänligen hänvisa till Jest-konfiguration.
- Skriv dina tester
Skapa testfiler i ditt projekt, vanligtvis namngivna med .test.js
-ändelsen. Jest kommer automatiskt upptäcka och köra dessa testfiler.
Här är ett exempel från Jest-dokumentationen:
Kör sedan kommandot för att köra testet:
Genom att följa dessa tre steg kommer du att ha en välkonfigurerad testmiljö för att genomföra end-to-end tester med jest-puppeteer.
Observera dock att detta bara är ett grundläggande exempel. För mer detaljerad information om miljökonfiguration, vänligen hänvisa till relevant dokumentation:
Vanligt använda API:er
I de kommande stegen kommer vi att förlita oss på API:er som tillhandahålls av Jest, Puppeteer och jest-puppeteer för att hjälpa oss i våra tester.
Jest erbjuder huvudsakligen API:er för att organisera tester och hävda förväntade resultat. Du kan utforska de specifika detaljerna i dokumentationen.
Puppeteers API:er är främst utformade för att interagera med webbläsare, och deras stöd för testning är kanske inte lika enkelt. Du kan hänvisa till Puppeteer-dokumentationen för att förstå de funktioner den tillhandahåller. I våra efterföljande testexempel kommer vi att täcka några vanliga användningsfall.
Eftersom Puppeteers API:er inte ursprungligen utformades för testning kan det vara utmanande att använda dem för att skriva tester. För att förenkla processen att skriva Puppeteer-tester innehåller jest-puppeteer ett inbäddat bibliotek kallat expect-puppeteer
. Det erbjuder en uppsättning korta och användarvänliga API:er. Biblioteket beskrivs i sin dokumentation, som introducerar olika nyttiga funktioner, som visas i exemplen nedan:
I följande exempel kommer vi att kombinera de API:er som tillhandahålls av dessa bibliotek för att slutföra våra testscenarion.
Nu är det dags att börja lära sig hur man skriver testkod med hjälp av vår enkla att-göra-app.
Den enkla att-göra-appen
Anta att vi har en enkel att-göra-app som körs på http://localhost:3000
, den huvudsakliga HTML-koden för denna app är som följer:
När vi utför end-to-end-testning strävar vi efter att täcka följande scenarier:
- När vi öppnar appens URL ska appen och dess data laddas korrekt.
- Kontrollknappen i objektet ska gå att klicka på.
- Klicka på en extern länk inuti objektanteckningarna ska öppna länken i en ny flik.
- Kan lägga till ett objekt från formuläret.
Senare kommer vi att skriva testkod för att validera dessa scenarier.
Förvänta dig att appen och dess data har laddats
Vi anser att appen har laddats framgångsrikt när följande villkor är uppfyllda:
- Ett element med ID:t "app" bör finnas på sidan efter att man har öppnat appens URL.
- Datat inuti appen bör vara korrekt renderad.
Så vi har skrivit följande testkod:
I koden:
page.goto
är motsvarande att skriva "http://localhost:3000" i webbläsaren, så att du kan navigera till vilken URL som helst.page.waitForSelector
används för att vänta på att en specifik CSS-selektor ska matcha ett visst element, med en default väntetid på 30 sekunder.expect(page).toMatchElement
kommer frånexpect-puppeteer
lib, Det är liknandepage.waitForSelector
, men det stöder också att matcha texten inom ett element. Dessutom är standardtimeouten förtoMatchElement
endast 500 ms.
Det kan verka perfekt vid första anblicken, men när vi kör detta test och distribuerar det till en CI-miljö misslyckas det ibland efter flera körningar. Felmeddelandet indikerar:
Baserat på felinformationen och det faktum att detta test passerar oftast, kan vi dra slutsatsen att datan som begärs av appen kanske inte alltid returneras inom de första 500 ms efter att appen laddas. Därför vill vi vänta på att datan ska laddas innan vi gör påståendet.
Vanligtvis finns det två vanliga metoder för att uppnå detta:
- Öka assertions väntetid
Från felmeddelandet kan vi se att standardväntetiden för toMatchElement
är inställd på 500 ms. Vi kan öka väntetiden genom att lägga till timeout
-alternativet till funktionen på detta sätt:
Den här metoden kan minska förekomsten av misslyckade tester till viss del, men den löser inte helt problemet eftersom vi inte kan veta säkert hur mycket tid som behövs för att datan ska hämtas.
Därför använder vi bara denna metod när vi är säkra på den nödvändiga väntetiden, som i scenarion som "en tooltip visas efter att ha svävat över ett element i mer än 2 sekunder.”
- Vänta på att nätverksförfrågan ska slutföras innan du gör en assertion
Detta är rätt metod. Även om vi kanske inte vet hur länge datan kommer att ta, är det alltid ett säkert val att vänta på att nätverksförfrågan ska avslutas. Vid denna punkt kan vi använda page.waitForNavigation({ waitUntil: 'networkidle0' })
för att vänta på att nätverksförfrågningarna ska slutföras:
På detta sätt kan vi innan vi utför påståendet för Appen och dess laddade data säkerställa att våra nätverksförfrågningar redan har avslutats. Detta säkerställer att testet konsekvent ger rätt resultat.
Förvänta dig att klicka på en specifik knapp
Nästa test är att se om det går att klicka på check-knappen inuti ett objekt.
I exempelappen har vi märkt att alla objekt delar samma struktur. Deras check-knappar har CSS-selectorn li[class$=item] > button
, och knappens text är alltid "Check". Detta innebär att vi inte direkt kan specificera vilken items check-knapp vi ska klicka på. Därför behöver vi en ny lösning.
Anta att vi vill klicka på check-knappen för "Läsa Logto kom igång-dokumentet" objektet. Vi kan dela upp detta i två steg:
- Först få en referens till det specifika objektet.
- Sedan klicka på check-knappen inuti det objektet.
Som visas i koden använder vi funktionen toMatchElement
för att matcha objektet med itemName
inställt på "Läsa Logto kom igång-dokumentet". Sedan använder vi referensen readDocItem
för att klicka på knappen med textinnehållet 'Check' som finns under den.
Det är viktigt att notera att när vi får readDocItem
, är CSS-selectorn som används li[class$=item]:has(div[class$=itemName])
. Denna selektor matchar rot li
elementet i objektet, inte itemName
inuti det, eftersom vi har för avsikt att klicka på button
under li
taggen senare.
Användningen av expect(readDocItem).toClick
är liknande toMatchElement
. I exempel-koden skickar vi { text: 'Check' }
för att ytterligare matcha textContent
av knappen. Dock kan du välja om du vill matcha textinnehållet i knappen baserat på dina behov.
Förvänta dig att en extern länk öppnas i en ny flik
Nästa test är att se om det går att öppna en extern länk i en ny flik när man klickar på den inom anteckningarna.
I exempelappen har vi funnit att "Läsa Logto kom igång-dokumentet" objektet har en extern länk till Logto-dokumentet inom sina anteckningar. Här är vår testkod:
I koden utnyttjar vi toClick
för att klicka på länken inom itemNotes
.
Därefter använder vi browser.waitForTarget
för att fånga en nyligen öppnad flik med URL:en "https://docs.logto.io/docs/tutorials/get-started."
Efter att ha erhållit fliken använder vi target.page()
för att få en instans av Logto-dokumentets sida och kontrollera ytterligare om sidan har laddats.
Kom också ihåg att stänga den nyöppnade sidan efter att testet har slutförts.
Förvänta dig att skapa ett objekt från formuläret
Nu vill vi testa funktionen att lägga till ett objekt.
Vi behöver fylla i objektets namn och anteckningar i formuläret, klicka på "Add"-knappen och kontrollera om innehållet vi lagt till visas i listan:
Du kommer att märka att vi använder funktionen expect(page).toFill(inputSelector, content)
för att fylla i formulärinnehållet. Denna funktion ersätter allt innehåll i inmatningen med content
.
Om du vill lägga till några tecken till inmatningen utan att ersätta det befintliga innehållet kan du använda page.type(selector, content)
för att direkt lägga till önskat innehåll till inmatningen.
Men när vi har många fält att fylla i vår form är det inte bekvämt att kalla toFill
flera gånger. I detta fall kan vi använda följande metod för att fylla i allt innehåll i en kallelse:
Det angivna objektnyckeln är name-attributet för input-elementet.
Sammanfattning
Vi har täckt ett enkelt exempel för att introducera vanliga testkrav och motsvarande kodskrivningstekniker vid användning av jest-puppeteer för end-to-end testning. Vi hoppas att det kan hjälpa dig snabbt greppa grunderna för att skriva jest-puppeteer testkod.