Definitieve gids voor het testen van eenheden in React-toepassingen met Jest en React-testen

Introductie

Als ontwikkelaar zou een van de dingen die bovenaan uw lijst moeten staan, het verzenden van bugvrije code zijn. Niets is erger dan er donderdagavond achter te komen dat de wijzigingen die je op maandag hebt aangebracht, de live applicatie hebben verbroken. De enige manier om ervoor te zorgen dat uw app werkt volgens zowel de systeem- als de gebruikersvereisten, is door test het!

Testen is een cruciaal onderdeel van elke levenscyclus van softwareontwikkeling en zorgt ervoor dat een stuk software correct en volgens plan werkt. Webontwikkeling, ontwikkeling van mobiele apps en, belangrijker in onze context, React-applicaties volgen allemaal dezelfde principes.

React-componenten kunnen op een aantal verschillende manieren worden getest, grofweg verdeeld in twee groepen:

  • Componentbomen weergeven in een eenvoudige testomgeving en uitspraken doen over hun prestaties
  • Hardlopen "eind-tot-eind testen", waarbij een volledige applicatie wordt getest in een realistische browseromgeving

Hoewel het testen van React-apps op een aantal manieren kan worden gedaan, zullen we in deze handleiding een React-app maken en een complete gids behandelen over hoe we unit-tests kunnen uitvoeren op de React-applicatie met behulp van er is en Reageer testbibliotheek zodat u uw testvaardigheden kunt aanscherpen en kunt leren hoe u een tamme React-toepassing kunt maken.

Opmerking: Je kunt toegang krijgen tot de repository voor deze gids en spelen met alles wat erin staat, door dit te gebruiken koppeling op GitHub.

Wat is testen?

Laten we allereerst de zaken in perspectief plaatsen. Testen is een zeer brede term en kan verwijzen naar handmatig testen, testen van eenheden, regressietesten, integratietesten, belastingtesten, enz.

In het kader van testen van een eenheid waar we ons vandaag op zullen concentreren - we testen de functie van onderscheidende eenheden, meestal op methodeniveau. Dit kan de numerieke waarden van de uitvoer testen, de lengte van uitvoerwaarden, hun vorm, hoe de methode reageert op ongeldige invoer, enz.

Aangezien de meeste goede softwarepraktijken pleiten voor korte, bruikbare methoden/functies die op zichzelf staan โ€‹โ€‹en een duidelijk doel hebben, zullen veel methoden andere methodes. Doorgaans wilt u zowel de interne methoden als de externe methoden testen, om er zeker van te zijn dat eventuele wijzigingen die u aanbrengt tijdens het herstructureren, oplossen van bugs of het verbeteren van een functie, geen andere functionaliteit verstoren.

In Testgestuurde ontwikkeling (TDD), wordt u aangemoedigd om een โ€‹โ€‹test en verwachte waarde te schrijven voordat u de logica voor een methode schrijft. Natuurlijk zal het in eerste instantie mislukken. Daarna laat je het echter gewoon werken, en als het de test doorstaat, begin je het te herstructureren om het korter, schoner, sneller, enz. Te maken. Zolang de output hetzelfde blijft, weet je dat je niets hebt gebroken tijdens het herstructureren!

Het schrijven van je eigen unit tests plaatst je in de mindset van iemand gebruik uw methoden, in plaats van iemand het schrijven van die methoden, die vaak helpen om een โ€‹โ€‹functie opnieuw te bekijken, omvatten extra controles en validatie en zoeken naar bugs. Soms leidt het tot ontwerpwijzigingen om de code te maken meer toetsbaar, zoals ontkoppelingsfunctionaliteit om numeriek testen voor elk afzonderlijk onderdeel mogelijk te maken.

Zodra een basislijn is vastgesteld en uw code de tests heeft doorstaan, kunt u wijzigingen aanbrengen en valideren dat de afzonderlijke eenheden (meestal methoden) afzonderlijk werken. Testen is vooral handig wanneer er updates zijn voor een codebase.

Benaderingen van testen

Testen kan op twee verschillende manieren: handmatig en webmaster.. Door rechtstreeks met een applicatie te interageren, verifieert handmatig testen of deze naar behoren functioneert. Geautomatiseerd testen is het schrijven van programma's om de controles voor u uit te voeren.

Handmatig testen

De meeste ontwikkelaars beoordelen hun code handmatig, omdat dit de snelste, meest natuurlijke en eenvoudigste manier is om snel een functionaliteit te testen.

Handmatig testen is de volgende logische stap die volgt na het schrijven van functionaliteit, net zoals het proeven van een gerecht komt na het op smaak brengen (een kenmerk toevoegen) om te controleren of het werkte zoals bedoeld.

Stel dat u als ontwikkelaar in loondienst een aanmeldingsformulier bouwt. Je sluit je teksteditor niet zomaar af en laat je baas weten dat het formulier na het coderen compleet is. U opent de browser, doorloopt het aanmeldingsformulier en zorgt ervoor dat alles volgens plan verloopt. Met andere woorden, je test de code handmatig.

Handmatig testen is ideaal voor kleine projecten en u hebt geen geautomatiseerde tests nodig als u een takenlijsttoepassing heeft die u elke twee minuten handmatig kunt controleren. Echter, afhankelijk van handmatig testen wordt moeilijk naarmate uw app groeit โ€“ het zou maar al te gemakkelijk kunnen worden om de concentratie te verliezen en misschien iets te vergeten te controleren. Met een groeiende lijst van op elkaar inwerkende componenten, wordt handmatig testen nog moeilijker, vooral als je iets hebt getest en bent doorgegaan naar een nieuw item en de laatste functie hebt verbroken, zodat je het een tijdje niet opnieuw test zonder te weten dat het nu kapot is.

Simpel gezegd, handmatig testen is prima om mee te beginnen, maar schaalt niet goed en garandeert geen codekwaliteit voor grotere projecten. Het goede nieuws is dat computers geweldig zijn in dit soort taken, en dat hebben we te danken aan geautomatiseerd testen!

Geautomatiseerde tests

Bij geautomatiseerd testen schrijf je extra code om je applicatiecode te testen. Nadat je de testcode hebt geschreven, kan het test uw app zo vaak als u wilt met minimale inspanning.

Er zijn tal van technieken voor het schrijven van geautomatiseerde tests:

  • Programma's schrijven om een โ€‹โ€‹browser te automatiseren,
  • Functies rechtstreeks vanuit uw broncode aanroepen,
  • Schermafbeeldingen van uw gerenderde toepassing vergelijken...

Elke techniek heeft zijn eigen voordelen, maar ze hebben allemaal รฉรฉn ding gemeen: ze besparen u tijd en zorgen voor een hogere codekwaliteit dan handmatig testen!

Geautomatiseerde tests zijn uitstekend om ervoor te zorgen dat uw applicatie presteert zoals gepland. Ze maken het ook gemakkelijker om codewijzigingen binnen een applicatie door te nemen.

Soorten testen

Tot nu toe hebben we testen op hoog niveau bekeken. Het wordt tijd om de verschillende soorten toetsen te bespreken die kunnen worden geschreven.

Er zijn drie soorten front-end applicatietests:

  • Eenheidstests: Bij unittests worden individuele units of componenten van de software getest. Een individuele eenheid is een enkele functie, methode, procedure, module, component of object. Een eenheidstest isoleert en verifieert een stuk code om te valideren dat elke eenheid van de softwarecode presteert zoals verwacht.
    Individuele modules of functies worden getest in unit testing om er zeker van te zijn dat ze naar behoren werken, en alle componenten worden ook afzonderlijk getest. Eenheidstest omvat bijvoorbeeld het bepalen of een functie, een instructie of een lus in een programma correct werkt.

  • Snapshot-testen: Dit type test zorgt ervoor dat de gebruikersinterface (UI) van een webtoepassing niet onverwacht verandert. Het legt de code van een component vast op een specifiek tijdstip, waardoor we de component in de ene toestand kunnen vergelijken met elke andere mogelijke toestand die het zou kunnen aannemen.
    Een typisch snapshot-testscenario omvat het renderen van een UI-component, het maken van een snapshot en het vergelijken van de snapshot met een referentie-snapshotbestand dat bij de test wordt bewaard. Als de twee momentopnamen verschillen, mislukt de test omdat de wijziging ofwel onverwacht was of omdat de referentiemomentopname moest worden bijgewerkt om de nieuwe UI-component weer te geven.

  • End-to-end testen: End-to-end tests zijn het gemakkelijkst te begrijpen type test. End-to-end tests in front-end applicaties automatiseren een browser om ervoor te zorgen dat een applicatie correct werkt vanuit het perspectief van de gebruiker.
    End-to-end testen besparen veel tijd. U kunt een end-to-end-test zo vaak als u wilt uitvoeren nadat u deze hebt geschreven. Overweeg hoeveel tijd een reeks van honderden van deze tests zou kunnen besparen in vergelijking met het schrijven van tests voor elke afzonderlijke eenheid.
    Met alle voordelen die ze met zich meebrengen, hebben end-to-end-tests een paar problemen. Om te beginnen zijn end-to-end tests tijdrovend. Een ander probleem met end-to-end-tests is dat ze moeilijk te debuggen zijn.

Opmerking: Om reproduceerbaarheidsproblemen te voorkomen, kunnen end-to-end tests worden uitgevoerd in een reproduceerbare omgeving, zoals een Docker container. Docker-containers en end-to-end-tests vallen buiten het bestek van deze handleiding, maar u moet er naar kijken als u end-to-end-tests wilt uitvoeren om het probleem van storingen op verschillende machines te voorkomen.

Als u de basisprincipes van end-to-end testen met Cypress wilt begrijpen, lees dan onze โ€œEnd-to-end testen in JavaScript met Cypressโ€!

Voordelen en nadelen van testen

Hoewel testen belangrijk is en zoals gewoonlijk zou moeten worden gedaan, heeft het zowel voor- als nadelen.

voordelen

  • Het beschermt tegen onverwachte regressie
  • Goed testen verhoogt de kwaliteit van de code aanzienlijk
  • Het stelt de ontwikkelaar in staat zich te concentreren op de huidige taak in plaats van op het verleden
  • Het maakt de modulaire opbouw van anders moeilijk te bouwen applicaties mogelijk
  • Het elimineert de noodzaak voor handmatige verificatie

Nadelen

  • Je moet meer code schrijven naast het debuggen en onderhouden ervan, en velen hebben het gevoel dat het onnodige overhead is in kleinere projecten, ongeacht de voordelen
  • Niet-kritieke/goedaardige testfouten kunnen ertoe leiden dat de app wordt afgewezen tijdens continue integratie

Overzicht van unittesten

Tot nu toe hebben we gekeken naar testen in het algemeen. Dit is het moment om te duiken in alles wat te maken heeft met het testen van eenheden en het schrijven van eenheidstests in React-applicaties!

Voordat we het testen van eenheden definiรซren, is het noodzakelijk dat we tot de kennis komen dat een goede testmethode heeft tot doel de ontwikkelingstijd te versnellen, bugs in een applicatie te verminderen en de kwaliteit van code te verbeteren, terwijl een slechte testbenadering een applicatie zou verlammen. Als gevolg hiervan zouden we als softwareontwikkelaars effectieve benaderingen voor het testen van eenheden moeten leren, en een daarvan is het testen van eenheden.

Een eenvoudige definitie van testen is dat het het proces is waarbij wordt gecontroleerd of een toepassing zich correct gedraagt. Unit testing is het proces van het uitvoeren van tests tegen de componenten of functies van een applicatie. Unit-tests zijn functies die geรฏsoleerde versies van de functies in uw broncode aanroepen om te controleren of ze zich deterministisch gedragen zoals ze zouden moeten werken.

Voordelen van eenheidstests

Unit-tests zijn snel en kunnen in een paar seconden worden uitgevoerd (hetzij individueel voor een nieuwe functie, hetzij globaal alle tests uitvoeren), waardoor ontwikkelaars onmiddellijk feedback krijgen of een functie al dan niet kapot is. Ze helpen ook bij het verstrekken van documentatie, want als een nieuwe ontwikkelaar zich bij een project aansluit, moeten ze weten hoe verschillende eenheden van de codebase zich gedragen; dit kan worden geweten door te kijken naar de resultaten van unit-tests.

Nadelen van eenheidstests

Hoewel unit-tests hun goede kanten hebben, hebben ze ook hun eigen problemen. Een probleem is dat refactoring code als het gaat om ontwerpwijzigingen, aangezien deze meestal moeilijker zijn bij unit-tests. Stel dat u bijvoorbeeld een gecompliceerde functie heeft met zijn unit-tests en die functie wilt opsplitsen in meerdere modulaire functies. Een unit-test zal waarschijnlijk mislukken voor die functie, en u moet deze afschaffen en twee unit-tests schrijven voor de split-functies. Dit is de reden waarom het testen van eenheden impliciet aanmoedigt om ze vooraf te splitsen en afzonderlijk te testen, wat leidt tot meer testbare, modulaire codecomponenten. Desalniettemin kunt u in sommige gevallen de mogelijke veranderingen in de loop van de tijd niet voorzien, en de hoeveelheid tijd die nodig is om de unit-tests bij te werken, maakt serieuze refactoring-processen minder aantrekkelijk.

Een ander probleem met unit testing is dat het alleen individuele onderdelen van een app controleert, zelfs als dat onderdeel een logische combinatie is van meerdere kleinere onderdelen. hoofdstuk toets voor de gehele aanvraag. Afzonderlijke onderdelen van een app werken mogelijk naar behoren, maar als niet wordt getest hoe ze zich in combinatie gedragen, kunnen de tests onbruikbaar worden. Daarom moeten unit-tests worden aangevuld met end-to-end-tests of integratietests, of idealiter beide.

Eenheid die een React-toepassing test - Demoproject

Laten we eens kijken naar een realistisch voorbeeld van het testen van eenheden van een React-toepassing!

In deze demo testen we een Counter-app met veel verschillende onderdelen. Ondanks dat het klinkt als een vrij eenvoudige app, zou het een goed voorbeeld zijn om te leren hoe het testen van eenheden werkt. De essentie van het testen van deze app is dat er verschillende aspecten van het onderdeel zijn die afhangen van hoe de gebruiker ermee omgaat.

Project Setup

De create-react-app commando, gebouwd door het React-team, is de beste manier om aan de slag te gaan met het maken van een real-world en grootschalige React-applicatie omdat het klaar is voor gebruik en gemakkelijk werkt met de Jest-testbibliotheek. Als je de package.json bestand, zult u zien dat we standaard ondersteuning hebben voor er is en Testbibliotheek reageren in de setupTests.js het dossier. Dit elimineert de noodzaak voor ons om Jest handmatig in ons project te installeren als dat nodig is!

Als je het nog niet hebt gebruikt, voer het dan uit met npx, die het zal installeren voor later gebruik:

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

Als je de tool al hebt geรฏnstalleerd, maak dan een React-app en geef deze een naam react-unit-tests:

$ create-react-app react-unit-tests

Opmerking: npx gebruikt de laatste versie van create-react-app, terwijl een wereldwijd geรฏnstalleerde versie dat misschien niet doet. Het wordt over het algemeen aangeraden om de tool door te nemen npx om zeker te zijn van de nieuwste versies, tenzij u doelbewust een andere versie wilt gebruiken.

Vervolgens gaan we naar de projectdirectory en starten we de ontwikkelserver:

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

Dit zal onze nieuw gemaakte app in de browser uitvoeren op localhost:3000.

Opmerking: Een handige functie hier is dat hot reloading wordt standaard ondersteund, dus het is niet nodig om de browser steeds opnieuw te laden om nieuwe wijzigingen te zien of handmatig te installeren nodemon of soortgelijke bibliotheken.

De tegencomponent bouwen

In het src directory van ons project, maak een nieuw bestand met de naam Counter.js. in Counter.js, zullen we alle onderdelen van de component definiรซren. Het bevat verschillende functies van de teller, waaronder increment(), decrement(), restart() en switchSign(), waarmee de telwaarde wordt omgekeerd van negatief naar positief wanneer erop wordt geklikt. Deze functies zijn gemaakt voor het manipuleren van de initiรซle telwaarde (die wordt doorgegeven als een 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;

Werk dan bij App.js:


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

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

export default App;

Nu kunnen we de teller-app in de browser bekijken:

Definitieve gids voor unit-testen in React-applicaties met Jest and React-Testing PlatoBlockchain Data Intelligence. Verticaal zoeken. Ai.

Tests maken voor componenten

Laten we een testbestand maken met de naam Counter.test.js om de test voor de tellercomponent weer te geven. Zorg ervoor dat u ook verwijdert App.test.js zodat het geen ongewenste resultaten oplevert terwijl we tests uitvoeren.

Opmerking: Het is gebruikelijk om uw testbestanden een naam te geven met het achtervoegsel .test.js, die de naam weerspiegelt van het bestand/de component die u aan het testen bent. Dit zorgt voor een continuรฏteit tussen testbestanden, er worden alleen wijzigingen aangebracht in de bestanden die relevant zijn voor de code die u bijwerkt bij het pushen van wijzigingen (lager aantal samenvoegconflicten) en is leesbaar.

Bovendien bevinden testbestanden zich doorgaans in een /test directory parallel naar de hoofdmap van uw broncode, maar dit is ook teamafhankelijk.

In Counter.test.js, importeren we eerst de Counter component, start dan de test met de describe() functie om alle verschillende functionaliteiten te beschrijven die binnen de component kunnen voorkomen.

De describe() functie wordt gebruikt om specifieke reeksen tests te groeperen die op een component kunnen plaatsvinden met behulp van verschillende it() en test() methoden. Het is een soort logische omhulling, waarin je, nou ja, beschrijft wat een reeks tests met elk doet it() een functionele test zijn voor een eenheid.

Het testen van uw React-componenten kan op een zodanige manier worden gedaan dat we een test-renderer kunnen gebruiken om snel een serialiseerbare waarde voor uw React-boom te creรซren in plaats van de grafische gebruikersinterface te genereren, waarvoor de volledige app moet worden gemaakt.

Testen van de initiรซle tellerwaarde

Bij het testen helpt het om een โ€‹โ€‹systematische lijst met kenmerken en aspecten van een bepaald kenmerk te maken โ€“ de toestanden waarin componenten zich kunnen bevinden, wat ze mogelijk kunnen beรฏnvloeden, enz.

Het eerste wat we gaan testen is de initiรซle telwaarde en hoe het onderdeel omgaat met de steun die het instelt. Met de it() methode controleren we of de teller-app daadwerkelijk de exacte initiรซle telwaarde weergeeft die is doorgegeven als een prop, dat wil zeggen 0 in dit geval, en geef een callback-functie door die alle acties beschrijft die binnen de test zullen plaatsvinden:


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);
  });
});

Hier gebruikten we de screen instantie uit de React Testing-bibliotheek om de component weer te geven voor testdoeleinden. Het is handig om een โ€‹โ€‹nepversie van een te testen component weer te geven. En sinds de

element dat de bevat count waarde gebonden is aan dynamisch veranderen, gebruiken we de screen.getByTestId() functie om ernaar te luisteren en de waarde op te halen met de textContent eigendom.

Bekijk onze praktische, praktische gids voor het leren van Git, met best-practices, door de industrie geaccepteerde normen en bijgevoegd spiekbriefje. Stop met Googlen op Git-commando's en eigenlijk leren het!

Opmerking: De screen functie retourneert een overeenkomend DOM-knooppunt voor elke query of genereert een fout als er geen element wordt gevonden.

Vervolgens in de Counter.js component, zullen we luisteren naar de

element tijdens het testen door a in te stellen data-testid attribuut aan het element met een waarde 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;

Om te testen of de initiaal count waarde is gelijk aan 0, wij gebruiken de expect() methode om te beschrijven wat er wordt verwacht van de test die we hebben ingesteld! In ons geval verwachten we dat de initiรซle telwaarde is 0 dus gebruiken we de toEqual() methode, die wordt gebruikt om te bepalen of de waarden van twee objecten overeenkomen. In plaats van de identiteit van het object te bepalen, kan de toEqual() matcher controleert recursief alle velden op gelijkheid.

Opmerking: De test is specifiek gericht op de informatie die u weergeeft; in ons voorbeeld, dat wil zeggen de Counter onderdeel dat een heeft ontvangen initialCount prop. Dit suggereert dat zelfs als een ander bestand, laten we zeggen App.js-heeft ontbrekende rekwisieten in de Counter onderdeel, zal de test nog steeds slagen omdat deze uitsluitend is gericht op Counter.js en weet niet hoe het te gebruiken Counter bestanddeel. Bovendien, omdat de tests onafhankelijk van elkaar zijn, heeft het renderen van hetzelfde onderdeel met verschillende rekwisieten in andere tests ook geen invloed.

Nu kunnen we de ingestelde test uitvoeren:

$ yarn test

De test zou moeten mislukken:

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.

Deze test is mislukt omdat we een nummer tegen een string hadden getest, wat resulteerde in een diepe gelijkheidsfout. Om dat op te lossen, gegoten de textContent, dwz de beginwaarde, in onze callback-functie als een nummer:


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 zal onze code de eerste test doorstaan:

$ 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.

Dit is een eenvoudig voorbeeld van hoe testen en door logica te schrijven, kunt u problemen in de loop van de tijd voorkomen, voordat de technische schuld zich verder ophoopt. Voortijdig testen kan je ook opsluiten, aangezien refactoring en veranderende logica qua tijd duurder is als je ook tests moet herschrijven.

Het vinden van een goede balans kan helpen de kwaliteit van uw software te verbeteren, met een minimaal negatief effect op uw productiviteit en snelheid.

De verhogingsknop testen

Om te testen dat de increment knop werkt zoals het hoort, dat wil zeggen om de count elke keer dat er op wordt geklikt, รฉรฉn waarde verhogen, moeten we eerst toegang krijgen tot het increment knop, dan definiรซren we een nieuw it() methode voor hetzelfde.

Aangezien de waarde van de knop niet dynamisch is, heeft deze dus altijd de waarde Increment binnenin gebruiken we de getByRole() methode in plaats van de getByTestId() om de DOM te bevragen.

Bij gebruik van het getByRole() methode beschrijft een rol een HTML-element.

We moeten ook een object doorgeven om te definiรซren welke knop we in het bijzonder willen testen, aangezien er mogelijk veel knoppen zijn wanneer de DOM wordt weergegeven. In het object zetten we a name met een waarde die hetzelfde moet zijn als de tekst op de ophogingsknop.

Het volgende dat u moet doen, is een klikgebeurtenis simuleren met behulp van de fireEvent() methode, die het mogelijk maakt om gebeurtenissen te activeren die gebruikersacties simuleren tijdens het testen.

Eerst schrijven we een test om te zien of de telwaarde met 1 toeneemt vanaf de beginwaarde van 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);
  });
});

Dit resulteert in:

$ 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.

Dan kunnen we ook een test schrijven om te controleren of de count waarde was 0 voordat op de knop werd geklikt door er twee te definiรซren expect() methoden - รฉรฉn voordat de click-gebeurtenis wordt geactiveerd en een andere nadat de click-gebeurtenis wordt geactiveerd:


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);
});

De tests zijn nog steeds geslaagd:

$ 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.

De afnameknop testen

Op dezelfde manier schreven we de test voor de Increment knop, definiรซren we de test voor de Decrement knop als volgt:


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);
});

Dit resulteert in:

$ 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.

De herstartknop testen

Vergelijkbaar met de Increment en Decrement knoppen, definiรซren we de test voor de Restart knop als volgt:


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);
});

Voor testdoeleinden is de beginwaarde ingesteld op 50 (willekeurige waarde) en wanneer de test wordt uitgevoerd, slagen alle vier de tests met 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.

Testen van de Switch Sign-knop

We schrijven ook de test voor het omkeren van het teken op de count waarde door de waarde van in te stellen count tot 50 in het testbestand. Let vervolgens op welk teken wordt weergegeven voordat en nadat een klikgebeurtenis door de knop wordt geactiveerd:


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);
});

Dit resulteert in:

$ 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 tests zijn met goed gevolg afgelegd voor onze teller-app.

Het schrijven van tests is niet moeilijk - we simuleren effectief de use-cases van een functie om ervoor te zorgen dat deze niet kapot gaat wanneer deze wordt gebruikt zoals bedoeld en onbedoeld. Heeft iemand een waarde opgegeven die buiten de grenzen ligt? Een verkeerd formaat? De toepassing zou het probleem moeten oplossen in plaats van mislukken.

Over het algemeen is een goed uitgangspunt voor testen:

  • Test op beoogd gedrag (wat uw kenmerken ook zijn)
  • Test op alle facetten van onbedoeld gedrag (verkeerde invoer, zoals niet-ondersteunde indelingen, grenzen, enz.)
  • Test numeriek (als uw functie numerieke waarden produceert die kunnen worden geverifieerd, berekent u het resultaat handmatig en controleert u of het de juiste uitvoer retourneert)

Beste praktijken voor het testen van eenheden

  • Tests moeten deterministisch zijn: Het meerdere keren uitvoeren van dezelfde tests op hetzelfde onderdeel zou elke keer dezelfde resultaten moeten opleveren. U moet ervoor zorgen dat uw gegenereerde momentopnamen geen platformspecifieke of andere niet-deterministische gegevens bevatten.

  • Vermijd onnodige testen: Goede tests komen niet met onnodige verwachtingen of testgevallen.
    We kunnen een beter begrip krijgen door de onderstaande tests te bekijken:

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

Als we weten dat de succesboodschap binnen de succesmodal zichtbaar is, betekent dat dat de succesmodal zelf ook zichtbaar is. In dit geval kunnen we dus veilig de eerste test verwijderen en alleen de tweede uitvoeren, of ze combineren. Het hebben van veel testen kan een vals gevoel van veiligheid geven als ze overbodig zijn.

  • Vermijd het blootleggen van interne logica: Als uw test een actie uitvoert die uw gebruiker niet uitvoert (zoals het testen van een interne methode die niet zichtbaar is voor de gebruiker), test u waarschijnlijk implementatiedetails. U zou uiteindelijk een privรฉfunctie kunnen blootleggen om uw component te testen. Dit is een codegeur die moet worden vermeden. Herstructureer in plaats daarvan uw codebase zodat de privรฉfunctie kan worden getest zonder deze openbaar te maken.

  • Vermijd het testen van implementatiedetails: Als onze toepassing toeneemt x en y - of x eerst wordt opgehoogd of onwaarschijnlijk heeft geen betekenis, zolang het resultaat hetzelfde is. U moet implementatiedetails altijd kunnen herstructureren, wijzigen en anderszins bijwerken zonder de tests te breken, anders zouden tests de accumulatie van technische schulden katalyseren door de kosten van refactoring en optimalisatie te verhogen.

  • Plaats bedrijfslogica in pure functies in plaats van UI-componenten.

Conclusie

Deze gids gaat voornamelijk over het testen van eenheden. Het was echter belangrijk dat we eerst alles begrepen en waardeerden wat testen inhoudt, inclusief wat het betekent, benaderingen van testen, soorten testen en de voor- en nadelen ervan.

Het is van cruciaal belang om te onthouden waarom u tests schrijft terwijl u ze schrijft. Het doel van het schrijven van toetsen is meestal om tijd te besparen. Tests betalen zich uit als het project waaraan u werkt stabiel is en voor een lange tijd zal worden ontwikkeld. Hiermee kunnen we gerust zeggen dat het testen van een applicatie misschien niet de moeite waard is, als het je geen ontwikkelingstijd bespaart. Bovenal zijn goede tests eenvoudig te onderhouden en geven ze vertrouwen bij het wijzigen van uw code.

We hebben ook geleerd hoe we de DOM kunnen opvragen tijdens het testen van React-applicaties met behulp van de getByTestId() methode. Het is erg handig voor het definiรซren van containers en het opvragen van elementen met dynamische tekst, maar het zou niet uw standaardquery moeten zijn. In plaats van de getByTestId() methode meteen, probeer dan eerst een van deze:

  • getByRole() โ€“ het bevraagt โ€‹โ€‹een element en zorgt er tegelijkertijd voor dat het toegankelijk is met de juiste rol en tekst
  • getByLabelText() - het is een uitstekende query voor interactie met formulierelementen, het controleert ook of onze labels correct zijn gekoppeld aan onze inputs via de for- en id-attributen
  • getByText() โ€“ Als geen van de vorige twee zoekopdrachten beschikbaar is, wordt het getByText() methode zal nuttig zijn bij toegang tot elementen op basis van tekst die zichtbaar is voor de gebruiker
  • getByPlaceholderText(): Deze query heeft de voorkeur boven een test-ID wanneer u alleen een tijdelijke aanduiding hoeft te gebruiken om een โ€‹โ€‹element op te vragen.

We hopen dat deze gids nuttig voor u is! Je kunt toegang krijgen tot de repository voor deze gids en spelen met alles wat erin staat, door dit te gebruiken koppeling op GitHub.

Tijdstempel:

Meer van Stapelmisbruik