Definitiv guide til enhedstestning i React-applikationer med Jest og React-Test

Introduktion

Som udvikler burde en af ​​tingene øverst på din liste være at sende fejlfri kode. Intet kunne være værre end at finde ud af torsdag aften, at de ændringer, du foretog i mandags, brød live-applikationen. Den eneste måde at sikre, at din app fungerer i henhold til både system- og brugerkrav, er at test det!

Test er en afgørende komponent i enhver softwareudviklings livscyklus og sikrer, at et stykke software fungerer korrekt og efter planen. Webudvikling, udvikling af mobilapps og, endnu vigtigere i vores sammenhæng, React-applikationer følger alle de samme principper.

React-komponenter kan testes på et par forskellige måder, groft opdelt i to grupper:

  • Gengivelse af komponenttræer i et simpelt testmiljø og påstande om deres ydeevne
  • Løb "ende-til-ende test", som involverer test af en hel applikation i et realistisk browsermiljø

Mens test af React-apps kan udføres på en række måder, vil vi i denne vejledning oprette en React-app og dække en komplet guide til, hvordan vi kan udføre enhedstest på React-applikationen ved hjælp af der er , React Testing Library så du kan finpudse dine testfærdigheder og lære, hvordan du opretter en tam React-applikation.

Bemærk: Du kan få adgang til depotet for denne guide og lege med alt, hvad der er deri, ved at bruge dette link på GitHub.

Hvad er test?

Først og fremmest, lad os sætte tingene i et perspektiv. Test er et meget bredt begreb og kan referere til manuel test, enhedstest, regressionstest, integrationstest, belastningstest mv.

I forbindelse med enhedstest som vi vil fokusere på i dag – vi tester funktion af særprægede enheder, typisk på metodeniveau. Dette kan teste de numeriske værdier af output, længden af ​​outputværdier, deres former, hvordan metoden reagerer på ugyldig input osv.

Da de fleste god softwarepraksis taler for korte, handlingsrettede metoder/funktioner, der er selvstændige med et klart formål, vil mange metoder kalde andre metoder. Typisk vil du gerne teste både de interne metoder og eksterne metoder for at sikre, at eventuelle ændringer, du foretager, mens du omfaktorerer, retter fejl eller forbedrer en funktion, ikke bryder nogen anden funktionalitet.

In Testdrevet udvikling (TDD), opfordres du til at skrive en test og forventet værdi, før du skriver logikken for en metode. Det vil naturligvis mislykkes i starten. Derefter får du det dog bare til at virke, og når det består testen, begynder du at refaktorere det for at gøre det kortere, renere, hurtigere osv. Så længe output forbliver det samme, ved du, at du ikke har ødelagt noget under refaktorisering!

At skrive dine egne enhedstests sætter dig i nogens tankegang ved brug af dine metoder, snarere end nogen skrivning disse metoder, som ofte hjælper med at tage et nyt kig på en funktion, inkorporerer yderligere kontrol og validering og jagt efter fejl. Nogle gange fører det til designændringer for at lave koden mere testbar, såsom afkoblingsfunktionalitet for at muliggøre numerisk test for hver enkelt komponent.

Når en baseline er etableret, og din kode består testene, kan du foretage ændringer og validere, at de enkelte enheder (typisk metoder) fungerer individuelt. Test er især nyttigt, når der er opdateringer til en kodebase.

Tilgange til test

Test kan udføres på to forskellige måder: manuelt , automatisk. Ved at interagere direkte med en applikation verificerer manuel test, at den fungerer korrekt. Automatiseret test er praksis med at skrive programmer til at udføre kontrollerne for dig.

Manuel testning

De fleste udviklere gennemgår manuelt deres kode, da dette er den hurtigste, mest naturlige og enkleste måde at hurtigt teste en funktionalitet på.

Manuel test er det næste logiske trin, der følger efter skrivefunktionalitet, ligesom at smage en ret kommer efter at have krydret den (tilføje en funktion) for at kontrollere, om den virkede efter hensigten.

Antag, at du som ansat udvikler bygger en tilmeldingsformular. Du lukker ikke blot din teksteditor og informerer din chef om, at formularen er udfyldt efter kodning. Du åbner browseren, gennemgår tilmeldingsformularen og sørger for, at alt går som planlagt. Med andre ord, du vil manuelt teste koden.

Manuel test er ideel til små projekter, og du behøver ikke automatiserede tests, hvis du har en opgavelisteapplikation, som du kan tjekke manuelt hvert andet minut. Dog afhængig af manuel test bliver vanskelig, efterhånden som din app vokser – det kan blive alt for nemt at miste koncentrationen og glemme at tjekke noget, måske. Med en voksende liste over interagerende komponenter, bliver manuel test endnu sværere, især hvis du har testet noget, og er gået videre til et nyt emne og brudt den sidste funktion, så du ikke tester det igen i et stykke tid uden at vide, at det nu er i stykker.

Kort sagt, manuel test er okay for at starte - men skalerer ikke godt og garanterer ikke kodekvalitet til større projekter. Den gode nyhed er, at computere er fantastiske til opgaver som disse, vi har automatiseret test at takke!

automatiseret Test

I automatiseret test skriver du yderligere kode for at teste din applikationskode. Når du har skrevet testkoden, kan du test din app så mange gange du vil med minimal indsats.

Der er adskillige teknikker til at skrive automatiserede tests:

  • At skrive programmer til at automatisere en browser,
  • Opkaldsfunktioner direkte fra din kildekode,
  • Sammenligner skærmbilleder af din gengivne applikation...

Hver teknik har sit eget sæt af fordele, men de har alle én ting til fælles – de sparer dig tid og sikrer højere kodekvalitet i forhold til manuel test!

Automatiserede test er fremragende til at sikre, at din applikation fungerer som planlagt. De gør det også nemmere at gennemgå kodeændringer i en applikation.

Typer af testning

Indtil videre har vi set på test på et højt niveau. Det er ved at være tid til at diskutere de forskellige typer af prøver, der kan skrives.

Der er tre typer front-end-applikationstests:

  • Enhedstest: I enhedstests testes individuelle enheder eller komponenter i softwaren. En individuel enhed er en enkelt funktion, metode, procedure, modul, komponent eller objekt. En enhedstest isolerer og verificerer en kodesektion for at validere, at hver enhed af softwarens kode fungerer som forventet.
    Individuelle moduler eller funktioner testes i enhedstest for at sikre, at de fungerer korrekt, som de burde, og alle komponenter testes også individuelt. Enhedstest vil for eksempel omfatte at bestemme, om en funktion, en sætning eller en loop i et program fungerer korrekt.

  • Snapshot test: Denne type test sikrer, at en webapplikations brugergrænseflade (UI) ikke ændres uventet. Det fanger koden for en komponent på et bestemt tidspunkt, hvilket giver os mulighed for at sammenligne komponenten i en tilstand med enhver anden mulig tilstand, den kunne tage.
    Et typisk snapshot-testscenarie involverer gengivelse af en UI-komponent, at tage et snapshot og sammenligne snapshottet med en reference-snapshot-fil, der opbevares sammen med testen. Hvis de to snapshots er forskellige, mislykkes testen, fordi ændringen enten var uventet, eller referencesnapshottet skulle opdateres for at afspejle den nye UI-komponent.

  • End-to-end tests: End-to-end tests er den nemmeste type test at forstå. End-to-end-tests i frontend-applikationer automatiserer en browser for at sikre, at en applikation fungerer korrekt fra brugerens perspektiv.
    End-to-end test sparer meget tid. Du kan køre en ende-til-ende-test så mange gange du vil, efter du har skrevet den. Overvej, hvor meget tid en række af hundredvis af disse test potentielt kan spare sammenlignet med at skrive test for hver enkelt enhed.
    Med alle de fordele, de giver, har ende-til-ende-test et par problemer. Til at begynde med er ende-til-ende tests tidskrævende. Et andet problem med end-to-end-tests er, at de kan være svære at fejlfinde.

Bemærk: For at undgå problemer med reproducerbarhed kan ende-til-ende-test køres i et reproducerbart miljø, såsom en Docker container. Docker-containere og ende-til-ende-tests er uden for denne vejlednings omfang, men du bør undersøge dem, hvis du vil køre ende-til-ende-tests for at undgå problemet med fejl på forskellige maskiner.

Hvis du gerne vil forstå det grundlæggende i end-to-end test med Cypress - læs vores "Ende-til-ende-test i JavaScript med Cypress"!

Fordele og ulemper ved test

Selvom test er vigtigt og bør udføres, som sædvanligt, har det både fordele og ulemper.

Fordele

  • Det beskytter mod uventet regression
  • Korrekt test øger kodens kvalitet markant
  • Det giver udvikleren mulighed for at koncentrere sig om den aktuelle opgave frem for fortiden
  • Det muliggør modulopbygning af ellers svære at bygge applikationer
  • Det eliminerer behovet for manuel verifikation

Ulemper

  • Du skal skrive mere kode ud over fejlretning og vedligeholdelse, og mange føler, at det er unødvendigt overhead i mindre projekter, uanset fordelene
  • Ikke-kritiske/godartede testfejl kan resultere i, at appen bliver afvist under kontinuerlig integration

Oversigt over Unit Testing

Indtil videre har vi taget et kig på test generelt. Nu er det tid til at dykke ned i alt, hvad der vedrører enhedstestning, og hvordan man skriver enhedstests i React-applikationer!

Før vi definerer enhedstestning, er det nødvendigt for os at komme til den viden, at en god testmetode har til formål at fremskynde udviklingstiden, reducere fejl i en applikation og forbedre kvaliteten af ​​kode, mens en dårlig testmetode ville lamme en applikation. Som et resultat bør vi som softwareudviklere lære effektive enhedstestmetoder, og en af ​​dem er enhedstestning.

En simpel definition af test er, at det er processen med at kontrollere, at en applikation opfører sig korrekt. Enhedstest er processen med at køre test mod komponenterne eller funktionerne i en applikation. Enhedstest er funktioner, der kalder isolerede versioner af funktionerne i din kildekode for at verificere, at de opfører sig, som de skal, deterministisk.

Fordele ved enhedstests

Enhedstests er hurtige og kan køres på få sekunder (enten individuelt for en ny funktion eller globalt kører alle test), hvilket giver udviklere øjeblikkelig feedback om, hvorvidt en funktion er brudt. De hjælper også med at levere dokumentation, for hvis en ny udvikler slutter sig til et projekt, skal de vide, hvordan forskellige enheder i kodebasen opfører sig; dette kan vides ved at se på resultaterne af enhedstests.

Ulemper ved enhedstests

Mens enhedstests har deres gode sider, har de også deres egne problemer. Et problem er, at refactoring kode, når det kommer til designændringer, da disse har tendens til at være sværere med enhedstests. Lad os sige, at du for eksempel har en kompliceret funktion med dens enhedstest og ønsker at opdele den funktion i flere modulære funktioner. En enhedstest vil sandsynligvis mislykkes for den funktion, og du bliver nødt til at forælde den og skrive to enhedstests for de opdelte funktioner. Dette er grunden til, at enhedstest implicit opfordrer til at opdele dem på forhånd og teste dem individuelt, hvilket fører til mere testbare, modulære kodekomponenter. Ikke desto mindre kan du i nogle tilfælde ikke forudse de mulige ændringer, og den tid, det ville tage at opdatere enhedstestene, gør seriøse refaktoreringsprocesser mindre tiltalende.

Et andet problem med enhedstest er, at den kun kontrollerer individuelle dele af en app, selvom den del er en logisk kombination af flere mindre dele – der er ingen enhedstest for hele ansøgningen. Individuelle dele af en app fungerer muligvis korrekt, men hvis den måde, de opfører sig, når de kombineres, ikke testes, kan testene blive ubrugelige. Derfor bør enhedstests suppleres med end-to-end tests eller integrationstests eller ideelt set begge dele.

Enhed, der tester en React-applikation – Demoprojekt

Lad os tage et kig på et eksempel fra den virkelige verden på enhedstest af en React-applikation!

I denne demo vil vi teste en Counter-app med en masse forskellige dele. På trods af at det lyder som en ret simpel app, ville den tjene som et godt eksempel for at lære, hvordan enhedstest fungerer. Essensen af ​​at teste denne app er, at der er forskellige aspekter af komponenten, der afhænger af, hvordan brugeren interagerer med den.

Projektopsætning

create-react-app kommando, der er bygget af React-teamet, er den bedste måde at komme i gang med at skabe en reelt og storstilet React-applikation, fordi den er klar til brug og fungerer nemt med Spøg test bibliotek. Hvis du åbner package.json fil, vil du opdage, at vi har standardunderstøttelse for der er og React test bibliotek i setupTests.js fil. Dette eliminerer behovet for, at vi manuelt installerer Jest i vores projekt, hvis vi har brug for det!

Hvis du ikke allerede har brugt det – kør det med npx, som vil installere det til senere brug:

$ npx create-react-app react-unit-tests

Hvis du allerede har værktøjet installeret, skal du oprette en React-app og navngive den react-unit-tests:

$ create-react-app react-unit-tests

Bemærk: npx bruger den nyeste version af create-react-app, mens en globalt installeret version måske ikke. Det anbefales generelt at køre værktøjet igennem npx for at sikre de nyeste versioner, medmindre du bevidst ønsker at bruge en anden version.

Dernæst går vi ind i projektbiblioteket og starter udviklingsserveren:

$ cd react-unit-tests && npm start
// OR
$ cd react-unit-tests && yarn start

Dette vil udlæse vores nyoprettede app i browseren på localhost:3000.

Bemærk: En praktisk funktion her er det varm genindlæsning understøttes som standard, så der er ingen grund til at blive ved med at genindlæse browseren bare for at se nye ændringer eller manuelt installere nodemon eller lignende biblioteker.

Opbygning af tællerkomponenten

I src bibliotek for vores projekt, skal du oprette en ny fil kaldet Counter.js. I Counter.js, vil vi definere alle dele af komponenten. Det vil indeholde forskellige funktioner i tælleren, herunder increment(), decrement(), restart()og switchSign(), som inverterer tælleværdien fra negativ til positiv, når der klikkes på den. Disse funktioner er oprettet til at manipulere den indledende tælleværdi (som sendes ind som en prop):


import React, { useState } from "react";

function Counter({ initialCount }) {
  const [count, setCount] = useState(initialCount);

  const increment = () => {
    setCount((prev) => prev + 1);
  };

  const decrement = () => {
    setCount((prev) => prev - 1);
  };

  const restart = () => {
    setCount(0);
  };

  const switchSign = () => {
    setCount((prev) => prev * -1);
  };

  return (
    <div>
      <h1>
        Count: <h3>{count}</h3>
      </h1>
      <div>
        <button onClick={increment}>Increment</button>
        <button onClick={decrement}>Decrement</button>
        <button onClick={restart}>Restart</button>
        <button onClick={switchSign}>Switch sign</button>
      </div>
    </div>
  );
}

export default Counter;

Opdater derefter App.js:


import "./App.css";
import Counter from "./Counter";

function App() {
  return (
    <div className="App">
      <Counter />
    </div>
  );
}

export default App;

Nu kan vi se tæller-appen på browseren:

Definitive Guide to Unit Testing in React Applications with Jest and React-Testing PlatoBlockchain Data Intelligence. Vertical Search. Ai.

Oprettelse af test for komponenter

Lad os oprette en testfil kaldet Counter.test.js for at repræsentere testen for tællerkomponenten. Sørg også for at slette App.test.js så det ikke skaber uønskede resultater, mens vi kører tests.

Bemærk: En almindelig praksis er at navngive dine testfiler med et suffiks på .test.js, afspejler navnet på den fil/komponent, du tester. Dette sikrer en kontinuitet mellem testfiler, der kun foretages ændringer i de filer, der er relevante for den kode, du opdaterer, når du trykker på ændringer (lavere antal flettekonflikter) og er læsbar.

Derudover er testfiler typisk placeret i en /test Vejviser parallel til din kildekodes rodmappe, men dette er også teamafhængigt.

In Counter.test.js, importerer vi først Counter komponent, og start derefter testen med describe() funktion til at beskrive alle de forskellige funktionaliteter, der kan ske i komponenten.

describe() funktion bruges til at gruppere specifikke sæt af test, der kan forekomme på en komponent ved hjælp af forskellige it() , test() metoder. Det er en slags logisk indpakning, hvor du godt beskriver, hvad en række tests gør, med hver it() være en funktionstest for en enhed.

Test af dine React-komponenter kan udføres på en sådan måde, at vi kan bruge en testrenderer til hurtigt at skabe en serialiserbar værdi for dit React-træ i stedet for at generere den grafiske brugergrænseflade, hvilket ville involvere at skabe den komplette app.

Test af den oprindelige tællerværdi

Når man tester, hjælper det at lave en systematisk liste over funktioner og aspekter af en given feature – de tilstande som komponenter kan være i, hvad der kunne tænkes at påvirke dem osv.

Den første ting vi skal teste er indledende tælleværdi og hvordan komponenten håndterer den rekvisit, der sætter den. Med it() metode, kontrollerer vi, om tællerappen faktisk viser den nøjagtige indledende tælleværdi, der er blevet videregivet som en rekvisit, hvilket er 0 i dette tilfælde, og send en tilbagekaldsfunktion, der beskriver alle de handlinger, der vil forekomme i testen:


import { render, screen } from "@testing-library/react";
import Counter from "./Counter";

describe(Counter, () => {
  it("counter displays correct initial count", () => {
    render(<Counter initialCount={0} />);
    expect(screen.getByTestId("count").textContent).toEqual(0);
  });
});

Her brugte vi screen instans fra React Testing-biblioteket for at gengive komponenten til testformål. Det er nyttigt at gengive en mock version af en komponent, der skal testes. Og siden

element, der holder count værdi er bundet til at ændre sig dynamisk, vi bruger screen.getByTestId() funktion til at lytte til den og hente dens værdi med textContent ejendom.

Tjek vores praktiske, praktiske guide til at lære Git, med bedste praksis, brancheaccepterede standarder og inkluderet snydeark. Stop med at google Git-kommandoer og faktisk lærer det!

Bemærk: screen funktion returnerer en matchende DOM-node for enhver forespørgsel eller kaster en fejl, hvis intet element er fundet.

Derefter i Counter.js komponent, vil vi lytte til

element under testning ved at indstille en data-testid attribut til elementet med en værdi count:


import React, { useState } from "react";

function Counter({ initialCount }) {
  const [count, setCount] = useState(initialCount);
  const increment = () => {
    setCount((prev) => prev + 1);
  };
  const decrement = () => {
    setCount((prev) => prev - 1);
  };
  const restart = () => {
    setCount(0);
  };
  const switchSign = () => {
    setCount((prev) => prev * -1);
  };

  return (
    <div>
      <h1>
      	
        Count: <h3 data-testid="count">{count}</h3>
      </h1>
      <div>
        <button onClick={increment}>Increment</button>
        <button onClick={decrement}>Decrement</button>
        <button onClick={restart}>Restart</button>
        <button onClick={switchSign}>Switch sign</button>
      </div>
    </div>
  );
}

export default Counter;

For at teste om initialen count værdi er lig med 0, vi bruger expect() metode til at beskrive, hvad der forventes af den test, vi har sat! I vores tilfælde forventer vi, at den oprindelige tælleværdi er 0 så vi bruger toEqual() metode, som bruges til at bestemme, om værdierne af to objekter matcher. I stedet for at bestemme objektets identitet, toEqual() matcher kontrollerer rekursivt alle felter for lighed.

Bemærk: Testen er specifikt fokuseret på den information, du gengiver; i vores eksempel, det vil sige Counter komponent, der har modtaget en initialCount rekvisit. Dette tyder på, at selv hvis en anden fil-sige, lad os App.js-har manglende rekvisitter i Counter komponent, vil testen stadig bestå, fordi den udelukkende er fokuseret på Counter.js og ved ikke hvordan man bruger Counter komponent. Derudover, fordi testene er uafhængige af hinanden, vil gengivelse af den samme komponent med forskellige rekvisitter i andre test heller ikke påvirke.

Nu kan vi køre sættesten:

$ yarn test

Testen skulle mislykkes:

FAIL  src/Counter.test.js
  Counter
    × counter displays correct initial count (75 ms)

  ● Counter › counter displays correct initial count

    expect(received).toEqual(expected) // deep equality

    Expected: 0
    Received: "0"

       5 |   it("counter displays correct initial count", () => {
       6 |     render();
    >  7 |     expect(screen.getByTestId("count").textContent).toEqual(0);
         |                                                     ^
       8 |   });
       9 | });
      10 |

      at Object. (src/Counter.test.js:7:53)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        1.929 s, estimated 2 s
Ran all test suites related to changed files.

Denne test mislykkedes, fordi vi havde testet et tal mod en streng, hvilket resulterede i en dyb lighedsfejl. For at rette op på det, støbt og textContent, dvs. startværdien, i vores tilbagekaldsfunktion som et tal:


import { render, screen } from "@testing-library/react";
import Counter from "./Counter";

describe(Counter, () => {
  it("counter displays correct initial count", () => {
    render(<Counter initialCount={0} />);
     
    expect(Number(screen.getByTestId("count").textContent)).toEqual(0);
  });
});

Nu vil vores kode bestå den første test:

$ yarn test

PASS  src/Counter.test.js
  Counter
    √ counter displays correct initial count (81 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        2.271 s
Ran all test suites related to changed files.

Dette er et simpelt eksempel på, hvordan man tester mens skrivelogik hjælper dig med at undgå problemer ned ad linjen, før tech-gæld akkumuleres yderligere. At teste for tidligt kan også låse dig inde, da refaktorering og ændring af logik er dyrere tidsmæssigt, hvis du også skal omskrive tests.

At finde en god balance kan hjælpe med at opgradere din softwares kvalitet med en minimal negativ effekt på din produktivitet og hastighed.

Test af inkrement-knappen

For at teste, at increment knappen fungerer som den skal, det vil sige at øge count værdi med én, hver gang der klikkes på den, skal vi først have adgang til increment knap, så definerer vi en ny it() metode til det samme.

Da værdien af ​​knappen ikke er dynamisk, vil den altid have værdien Increment inde i den bruger vi getByRole() metode i stedet for getByTestId() for at forespørge DOM.

Når du bruger getByRole() metode, beskriver en rolle et HTML-element.

Vi skal også sende et objekt ind for at definere, hvilken knap, vi ønsker at teste, da der kan være mange knapper, når DOM gengives. I objektet sætter vi en name med en værdi, der skal være den samme som teksten på stigningsknappen.

Den næste ting at gøre er at simulere en klikhændelse ved hjælp af fireEvent() metode, som gør det muligt at affyre hændelser, der simulerer brugerhandlinger under test.

Først skriver vi en test for at se, om tælleværdien stiger med 1 fra dens begyndelsesværdi på 0:


import { fireEvent, render, screen } from "@testing-library/react";
import Counter from "./Counter";

describe(Counter, () => {
  it("counter displays correct initial count", () => {
    render(<Counter initialCount={0} />);
    expect(Number(screen.getByTestId("count").textContent)).toEqual(0);
  });

  it("count should increment by 1 if increment button is clicked", () => {
    render(<Counter initialCount={0} />);
    fireEvent.click(screen.getByRole("button", { name: "Increment" }));
    let countValue = Number(screen.getByTestId("count").textContent);
    expect(countValue).toEqual(1);
  });
});

Dette resulterer i:

$ yarn test

PASS  src/Counter.test.js
  Counter
    √ counter displays correct initial count (79 ms)
    √ count should increment by 1 if increment button is clicked (66 ms)

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        2.405 s
Ran all test suites related to changed files.

Så kan vi også skrive en test for at kontrollere, om count værdien var 0 før knappen blev klikket ved at definere to expect() metoder – en før klikhændelsen udløses og en anden efter klikhændelsen er udløst:


it("count should increment by 1 if increment button is clicked", () => {
    render(<Counter initialCount={0} />);
    let countValue1 = Number(screen.getByTestId("count").textContent);
    expect(countValue1).toEqual(0);
    fireEvent.click(screen.getByRole("button", { name: "Increment" }));
    let countValue2 = Number(screen.getByTestId("count").textContent);
    expect(countValue2).toEqual(1);
});

Prøverne bestod stadig:

$ yarn test

PASS  src/Counter.test.js
  Counter
    √ counter displays correct initial count (82 ms)
    √ count should increment by 1 if increment button is clicked (60 ms)

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        2.388 s
Ran all test suites related to changed files.

Test af dekrementeringsknappen

På samme måde skrev vi testen til Increment knappen, definerer vi testen for Decrement knap sådan:


it("count should decrement by 1 if decrement button is clicked", () => {
  render(<Counter initialCount={0} />);
  fireEvent.click(screen.getByRole("button", { name: "Decrement" }));
  let countValue = Number(screen.getByTestId("count").textContent);
  expect(countValue).toEqual(-1);
});

Dette resulterer i:

$ yarn test

PASS  src/Counter.test.js
  Counter
    √ counter displays correct initial count (79 ms)
    √ count should increment by 1 if increment button is clicked (73 ms)
    √ count should decrement by 1 if decrement button is clicked (21 ms)

Test Suites: 1 passed, 1 total
Tests:       3 passed, 3 total
Snapshots:   0 total
Time:        2.346 s
Ran all test suites related to changed files.

Test af genstartsknappen

Svarende til Increment , Decrement knapper, definerer vi testen for Restart knap sådan:


it("count should reset to 0 if restart button is clicked", () => {
  render(<Counter initialCount={50} />);
  fireEvent.click(screen.getByRole("button", { name: "Restart" }));
  let countValue = Number(screen.getByTestId("count").textContent);
  expect(countValue).toEqual(0);
});

Med henblik på testning blev startværdien sat til 50 (vilkårlig værdi), og når testen køres, består alle fire tests med succes:

$ yarn test

PASS  src/Counter.test.js
  Counter
    √ counter displays correct initial count (81 ms)
    √ count should increment by 1 if increment button is clicked (57 ms)
    √ count should decrement by 1 if decrement button is clicked (21 ms)
    √ count should reset to 0 if restart button is clicked (16 ms)

Test Suites: 1 passed, 1 total
Tests:       4 passed, 4 total
Snapshots:   0 total
Time:        2.583 s
Ran all test suites related to changed files.

Test af Switch Sign-knappen

Vi skriver også testen for at vende tegnet på count værdi ved at indstille værdien af count til 50 i testfilen. Så se efter, hvilket tegn der gengives før og efter en klikhændelse udløses af knappen:


it("count invert signs if switch signs button is clicked", () => {
  render(<Counter initialCount={50} />);
  let countValue1 = Number(screen.getByTestId("count").textContent);
  expect(countValue1).toEqual(50);
  fireEvent.click(screen.getByRole("button", { name: "Switch signs" }));
  let countValue2 = Number(screen.getByTestId("count").textContent);
  expect(countValue2).toEqual(-50);
});

Dette resulterer i:

$ yarn test

PASS  src/Counter.test.js
  Counter
    √ counter displays correct initial count (91 ms)
    √ count should increment by 1 if increment button is clicked (72 ms)
    √ count should decrement by 1 if increment button is clicked (21 ms)
    √ count should reset to 0 if restart button is clicked (19 ms)
    √ count invert signs if switch signs button is clicked (14 ms)

Test Suites: 1 passed, 1 total
Tests:       5 passed, 5 total
Snapshots:   0 total
Time:        3.104 s
Ran all test suites related to changed files.

Wooshh! Alle test er bestået med succes for vores tællerapp.

Det er ikke svært at skrive test – vi simulerer effektivt en funktions anvendelsestilfælde for at sikre, at den ikke går i stykker, når den bruges efter hensigten og utilsigtet. Har nogen givet en værdi uden for grænserne? Et forkert format? Applikationen bør løse problemet i stedet for at mislykkes.

Generelt er et godt udgangspunkt for test:

  • Test for tilsigtet adfærd (uanset hvad dine funktioner er)
  • Test for alle facetter af utilsigtet adfærd (forkerte input, såsom ikke-understøttede formater, grænser osv.)
  • Test numerisk (hvis din funktion producerer numeriske værdier, der kan verificeres, skal du beregne resultatet i hånden og kontrollere, om det returnerer det rigtige output)

Bedste praksis for enhedstestning

  • Tests skal være deterministiske: At køre de samme tests på den samme komponent flere gange bør give de samme resultater hver gang. Du skal sikre dig, at dine genererede snapshots ikke indeholder platformsspecifikke eller andre ikke-deterministiske data.

  • Undgå unødvendige tests: Gode ​​tests kommer ikke med unødvendige forventninger eller testcases.
    Vi kan finde en bedre forståelse ved at tage et kig på testene nedenfor:

test('the success modal is visible', () => {});
test('the success modal has a success message', () => {});

Hvis vi ved, at succesmeddelelsen i succesmodalen er synlig, betyder det, at selve succesmodalen også er synlig. Så i dette tilfælde kan vi sikkert fjerne den første test og kun udføre den anden eller kombinere dem sammen. At have mange tests kan give en falsk følelse af sikkerhed, hvis de er overflødige.

  • Undgå at afsløre intern logik: Hvis din test udfører en handling, som din bruger ikke gør (såsom at teste en intern metode, der ikke er eksponeret for brugeren), tester du højst sandsynligt implementeringsdetaljer. Du kan ende med at afsløre en privat funktion udelukkende for at teste din komponent. Dette er en kodelugt, som bør undgås. Omstrukturer i stedet din kodebase, så den private funktion kan testes uden at afsløre den offentligt.

  • Undgå at teste implementeringsdetaljer: Hvis vores ansøgning stiger x , y - om x øges først eller ikke sandsynligt har ingen betydning, så længe resultatet er det samme. Du bør altid være i stand til at refaktorere, ændre og på anden måde opdatere implementeringsdetaljer uden at bryde testene, ellers ville test katalysere teknologisk gældsopbygning ved at øge omkostningerne ved refactoring og optimering.

  • Placer forretningslogik i rene funktioner frem for UI-komponenter.

Konklusion

Denne vejledning handler primært om enhedstest. Det var dog vigtigt, at vi først forstod og værdsatte alt, hvad der omfatter test, herunder hvad det betyder, tilgange til test, typer af test og dets fordele og ulemper.

Det er vigtigt at huske, hvorfor du skriver prøver, mens du skriver dem. Typisk er målet med at skrive test at spare tid. Tests giver udbytte, hvis det projekt, du arbejder på, er stabilt og vil blive udviklet i lang tid. Med dette er det sikkert at sige, at test af en applikation kan ses som ikke det værd, hvis det ikke sparer dig udviklingstid. Frem for alt er gode tests enkle at vedligeholde og giver tillid, når du ændrer din kode.

Vi lærte også, hvordan man forespørger på DOM, mens vi testede React-applikationer ved hjælp af getByTestId() metode. Det er nyttigt til at definere containere og forespørge elementer med dynamisk tekst, men det bør ikke være din standardforespørgsel. I stedet for at bruge getByTestId() metode med det samme, prøv først en af ​​disse:

  • getByRole() – det forespørger på et element, samtidig med at det sikrer, at det er tilgængeligt med den korrekte rolle og tekst
  • getByLabelText() – det er en fremragende forespørgsel til at interagere med formularelementer, den kontrollerer også, at vores etiketter er korrekt forbundet med vores input via for og id attributterne
  • getByText() – Når ingen af ​​de to foregående forespørgsler er tilgængelige, getByText() metode vil være nyttig til at få adgang til elementer baseret på tekst, der er synlig for brugeren
  • getByPlaceholderText(): Denne forespørgsel er meget at foretrække frem for et test-id, når alt hvad du skal forespørge på et element er en pladsholder.

Vi håber, at denne guide er nyttig for dig! Du kan få adgang til depotet for denne guide og lege med alt, hvad der er deri, ved at bruge dette link på GitHub.

Tidsstempel:

Mere fra Stablemisbrug