Sådan tilføjes ende til slut test til dit projekt med Cypress

I dette indlæg går jeg igennem processen med at tilføje Cypress end-to-end-tests til et eksisterende projekt.

Foto af rawpixel på Unsplash

Hvorfor ende til ende test?

Der er fordele og ulemper ved alle testmetoder. Ende til ende-test er det tætteste på faktisk brugertest, hvilket er en af ​​dens største fordele. Jo tættere testen er at efterligne brugeren, desto mere sandsynligt vil den fange problemer, som brugeren måske oplever.

Hvis du ville have en bruger til at teste tweeting på Twitter, kan du fortælle dem noget i retning af:

Gå til https://twitter.com og log ind. Klik på tekstfeltet med pladsholder-teksten "Hvad sker der?", Og skriv derefter "Dette er en testtweet". Klik på knappen med teksten “Tweet”. Gå nu til din profilside og se på den første tweet. Teksten skal svare til ”Dette er en testtweet”.

Ideelt set giver du lignende instruktioner som din ende til slut testløber.

Du kan i stedet få det til at se efter elementer efter klassens navne eller id'er, men hvad nu hvis klassens navne eller id'er med vilje ændres? Eller hvad hvis teksten ændres ved et uheld? Hvis du har bedt testløberen om at klikke på knappen efter klassens navn, kunne testen forkert bestå. Du kan muligvis argumentere:

Hvad hvis du vil ændre teksten med vilje? Måske vil du ændre knappeteksten for at læse "Send" i stedet for "Tweet"?

Det er måske et gyldigt argument, men du kan også argumentere for, at du faktisk ønsker, at testen mislykkes, hvis teksten ændres. I sidste ende er du nødt til at spørge dig selv, "Hvis denne tekst er ændret, vil jeg, at mine test skal gå i stykker?" I tilfælde af "Send" kontra "Tweet", måske vil du ikke have, at testen skal bryde, men måske hvis tekst blev ved et uheld slettet eller stavet forkert, så vil du have dem til at bryde. Du kan ikke rigtig have begge dele, så du er nødt til at tage den bedste beslutning for dig og din app.

Nogle ulemper ved ende til slut test er:

  • De er "dyre", det vil sige, det tager lang tid at køre. Hver test kræver, at en fuld browser instantieres med faktiske browserhændelser, hvilket tager mere tid end enheds- eller integrationstest.
  • Det gør et godt stykke arbejde for at finde problemer, men det gør ikke et godt stykke arbejde at hjælpe dig med at løse disse problemer. Din ende til ende-test kan muligvis opleve, at betalingssystemet er ødelagt, men det fortæller dig ikke, hvilken af ​​dine 10 mikroservices der har forårsaget problemet.

Hvilken ende til ende test rammer at vælge

Der er en masse end-to-end testningsrammer derude, og det kan være vanskeligt at vælge den "rigtige". Jeg vil dele mine tanker meget kort, selvom jeg ganske vist kun har brugt Cypress:

Testcafé - Dette er den seneste ende til ende testrammer, og det ser ud til at være meget godt. Det integreres med Browser Stack, har god browserstøtte, har support til alle front-end rammer, understøtter ES2015 + syntaks og også typeskript. Det ser ud til, at du skal have den betalte version for at få registrerede test.

Puppeteer - Dette er Googles open source-løsning. Det virker let og let at komme i gang. Den er open source og kører på Chromium (hovedløs eller ej). Puppeteer er anlagt som en testramme, der har rig funktionalitet, bedre end at have ingen ende til slut test, men ikke en fuld løsning. De delte også for nylig, at de eksperimenterede med Firefox.

Cypress - Det er en udviklervenlig, open source testramme. Cypress registrerer snapshots og videoer af dine test, har en testløberkonsol og er gratis. Det er let at komme i gang for udviklere og QA-ingeniører. Det understøtter i øjeblikket kun Chrome-varianter, men det har cross browser support på køreplanen. Det har ikke native iframe-support, selvom der er løsninger. Cypress har sit eget løftebaserede system, som du skal bruge (kan ikke bruge ES6-løfter).

Her er en god ressource til en dybtgående sammenligning af Cypress og Testcafé: https://medium.com/yld-engineering-blog/evaluating-cypress-and-testcafe-for-end-to-end-testing-fcd0303d2103

Foto af chuttersnap på Unsplash

Kom godt i gang

Det projekt, jeg vil bruge, er https://ydkjs-exercises.com. Det er en web-applikation på én side, der indeholder øvelser, der er bygget til at hjælpe brugerne med at teste deres viden, når de læser You Don’t Know JavaScript. Det bruger React, React Router og React Context API. Der er enheds- / integrationstest ved hjælp af jest og react-testing-bibliotek. Og nu vil jeg tilføje ende til slut test med Cypress!

Jeg vil holde styr på udviklingen via tags, startende med cypress-0 og øge heltalet på hvert trin. Her er udgangspunktet.

Det første trin er at installere Cypress som en afhængighed:

npm installere cypress - save-dev

Den aktuelle version af Cypress er v3.1.1. Dokumenterne nævner, at Cypress npm-pakken er en indpakning omkring Cypress-binæren. Og at fra version 3.0 downloades den binære til et globalt cache-bibliotek, der skal bruges på tværs af projekter.

Lad os åbne Cypress. Hvis du bruger npm version> 5.2, kan du åbne den ved hjælp af:

npx cypress åben

Dette åbner Cypress med en velkomstmodal, der fortæller os, at de tilføjede en masse filer til vores projekt:

Når du har klikket for at lukke modalen, ser vi, at der er en række eksempler på tests, og vi ser, at vi kan køre dem i Chrome 70. Hvis du klikker på "Kørsler", ser du, at du kan oprette et Cypress-instrumentbræt for at se ved tidligere kørsler. Vi vil ikke bekymre dig om det, men du kan bestemt tjekke den funktion.

Jeg valgte at spore alle disse eksempelfiler i git, fordi jeg vil have, at fremtidige bidragydere skal have adgang til dem, når de gaffer projektet.

Her er de aktuelle fremskridt frem til dette punkt.

Skrivning af et cyprescript

Vi er næsten klar til at skrive vores første test. Vi er nødt til at oprette et bibliotek til at gemme vores Cypress-test: cypress / integration / ydkjs

Nu skal vi skrive det script, der starter vores dev-server, køre vores Cypress-test og derefter stoppe vores dev-server. Dette projekt blev bootstrapped med Create React App, hvilket betyder, at det har en scripts / start.js-fil, der bruges til at starte serveren. Jeg vil kopiere koden derfra, indsætte den i en ny scripts / cypress.js-fil og foretage nogle ændringer.

Kodestykket nedenfor er kødet fra vores nye scripts / cypress.js-fil.

returner devServer.listen (port, HOST, err => {
    hvis (fejle) {
        return console.log (err);
    }
    hvis (erInteraktiv) {
        clearConsole ();
    }
    console.log (chalk.cyan ('Start af udviklingsserveren ... \ n'));
    returnere cypress
        .løb({
            spec: './cypress/integration/ydkjs/*.js',
        })
        .then (resultater => {
            devServer.close ();
        });
});

Det gør bare, hvad vi sagde, det ville gøre. Det starter dev-serveren, kører alle testfiler i cypress / integration / ydkjs, og derefter stopper den dev-serveren.

Nu i cypress.json kan vi tilføje vores baseUrl:

{
    "baseUrl": "http: // localhost: 3000"
}

Nu kan vi skrive vores første test! Lad os kalde det cypress / integration / ydkjs / sidebar.js, og vi vil bruge det til at teste sidefeltets funktionalitet. Lad os lige nu skrive en dummy-test:

/ * global kontekst cy * /
/// 
context ('Sidebar', () => {
    beforeEach (() => {
        cy.visit ( '/');
    });
    
    it ('gør noget', () => {
        cy.concepts ('YDKJS-øvelser');
    });
});

Alt, hvad vi laver her, er at besøge base-url'en og finde et element, der indeholder “YDKJS-øvelser”. Bemærk, at jeg kun tilføjede kommentaren på den første linje, så eslint ikke klager over udefinerede Cypress-variabler.

Jeg tilføjede også et nyt script i min package.json:

"scripts": {
    ...
    "cypress": "nodeskripts / cypress.js",
    ...
},

Så nu kan jeg kalde npm run cypress, når jeg vil køre min end to end Cypress test. Nu, når jeg udfører denne kommando i terminalen, ser jeg, at min server starter, testen kører og passerer, og derefter stopper serveren. Woohoo!

Her er koden op til dette punkt.

Lad os skrive nogle rigtige tests!

Nu hvor vi har vores Cypress-script konfigureret til at starte serveren, køre testene og stoppe serveren, kan vi begynde at skrive nogle test!

Vi har allerede oprettet en sidebar.js testfil, så lad os skrive nogle test omkring vores sidebar-funktion. Måske bør vores første test være at teste for at sikre, at sidebjælken lukker, når vi klikker på X-knappen og åbner igen, når vi klikker på hamburgeren.

Inden vi finder X-knappen og klikker på den, så lad os sørge for, at sidepanelet er synligt, når du indlæser startsiden. Jeg kan lægge dette i metoden før hver, lige efter jeg navigerer til startsiden, fordi jeg altid vil sikre mig, at sidefeltet er synligt, når jeg først går til startsiden.

beforeEach (() => {
    cy.visit ( '/');
    . cy.contains (Progress) bør ( 'findes');
});

Lad os nu begynde at skrive testen. Da X faktisk er en SVG, kan vi ikke let bede Cypress om at gå og finde den. Så vi finder det ved hjælp af en data-testid attribut eller cy.get ("[data-testid = closeSidebar]"). Klik (). Jeg ved hvad du tænker ...

Okay, jeg forstår, at du ikke kan bruge tekst i dette tilfælde. Men hvorfor bruge en dataattribut? Hvorfor ikke bare bruge et klassens navn eller et id?

Den bedste praksis er at bruge en dataattribut. Du kan bruge klasserne, men de kan ændres og optimeres bedst til styling.

Hvad angår id'er, er hovedproblemet der, at du kun kan have en pr. Side, hvilket kan være irriterende. Hvad hvis du vil have alle X-knapper på siden og hævde, at der skal være 2 af dem? Du kan ikke gøre det let ved hjælp af id'er.

Vores afsluttede test ser måske sådan ud:

det ('lukkes, når X klikkes og åbnes igen, når der klikkes på hamburger', () => {
    . Cy.get ( '[data-testid = closeSidebar]') klik ();
    cy.contains (Progress) bør ( 'not.exist.');
    . Cy.get ( '[data-testid = openSidebar]') klik ();
    . cy.contains (Progress) bør ( 'findes');
});

Jeg går til startsiden, sørger for, at sidepanelet er åbent, klik derefter på X-knappen, og sørg for, at den er lukket, klik derefter på hamburgeren, og sørg for, at sidepanelet åbnes igen. Når vi kører det, passerer det!

Og du kan se en video af testen i cypress / ydkjs / sidebar.js.mp4! Temmelig pæn. Dette er super nyttigt, når dine test mislykkes, og du ikke ved hvorfor.

Én ting, du skal være forsigtig med, er, at Cypress er et løftebaseret system. Når du udfører cy.concepts ('Progress'). Should ('not.exist'), vil Cypress ikke gå videre til den næste kodelinje, før denne linje er sand. Hvis det ser et DOM-element, der indeholder 'Fremskridt', venter det, indtil det forsvinder, eller indtil det udløber, og testen mislykkes.

Dette system er pænt, fordi det gør det hurtigt og nemt at skrive disse test. Det kan dog bide dig nogle gange, når du har at gøre med asynkrone handlinger. Måske vil du sikre dig, at et DOM-element ikke vises som et resultat af at klikke på en knap. Du kan bare klikke på knappen og derefter kontrollere, om det DOM-element findes? Men hvad nu hvis DOM-elementet oprettes et sekund efter at have klikket på knappen? Din test ville bestå, når den skulle have mislykket.

Lad os skrive en anden test.

Når vi klikker på en bog på sidebjælken, ønsker vi at navigere til den side, der er knyttet til den bog.

it ('navigerer til / opadgående når Up & Going vælges', () => {
    cy.concepts (/ Up & Going \ (/). klik ({force: true});
    cy.url (). should ('include', '/ up-going');
    cy.concepts ('Kapitel 1: Into Programming'). skulle ('eksistere');
    cy.concepts ('Kapitel 2: I JavaScript'). should ('exist');
});

Der er et par ting at bemærke vedrørende denne test. På ydkjs-øvelsens hjemmeside er teksten “Op & Går” to steder. En gang i sidebjælken og en gang i midten af ​​siden. På sidebjælken er den fulde tekst “Op & Går (0/41)”, hvilket betyder, at brugeren har besvaret 0 spørgsmål ud af 41 mulige. På hovedsiden er teksten bare “Op & Går”. Så for at sikre, at vi klikker på Up & Going fra sidebjælken, bruger jeg regex til at klikke på elementet, der indeholder “Up & Going (”. Jeg vil ikke, at det skal indeholde 0 eller 41, fordi disse numre kunne ændres Dette kan være et af de tilfælde, hvor brugen af ​​en dataattribut muligvis er bedre end at bruge teksten, som jeg gjorde i kodestykket ovenfor.

Jeg er nødt til at tvinge klikhændelsen, fordi ankermærket har teksten, men det er indpakket af et element i listen. Herefter tester jeg for at sikre, at url'en er korrekt, og indholdet på siden er korrekt.

Dette er den endelige tilstand af koden.

Konklusion

Som du kan se, når du først har installeret Cypress, har du det rigtige script oprettet til at starte din dev-server, og du kommer til at skrive testene, og det er ret hurtigt og smertefrit at arbejde med Cypress.

Når du er fortrolig med det, kan du endda gøre din testkode genanvendelig ved at lave dine egne brugerdefinerede Cypress-kommandoer!

Du kan køre disse test forudgående eller i et CI-miljø for at sikre, at ingen regressioner kommer hen i produktionen.

Generelt er Cypress et perfekt solidt valg, hvis du vil tage din testning til det næste niveau med nogle end-to-end-tests!

God kodning!