Sådan starter du enhedstest af din JavaScript-kode

Vi ved alle, at vi skal skrive enhedstest. Men det er svært at vide, hvor man skal starte, og hvor meget tid der skal afsættes til test i forhold til den faktiske implementering. Så hvor skal man starte? Og handler det kun om testkode, eller har enhedstest andre fordele?

I denne artikel vil jeg forklare de forskellige typer tests, og hvilke fordele enhedstest bringer til udviklingsteams. Jeg vil fremvise Jest - en JavaScript-testramme.

Forskellige typer af test

Inden vi dykker ind i enhedstestspecifikationer, vil jeg lave en hurtig gennemgang af de forskellige typer tests. Der er ofte en vis forvirring omkring dem, og jeg er ikke overrasket. Nogle gange er linjen mellem dem ret tynd.

Enhedstest

Enhedstest tester kun en enkelt del af din implementering. En enhed. Ingen afhængigheder eller integrationer, ingen rammespecifikke. De er som en metode, der returnerer et link på et bestemt sprog:

export function getAboutUsLink(language){ switch (language.toLowerCase()){ case englishCode.toLowerCase(): return '/about-us'; case spanishCode.toLowerCase(): return '/acerca-de'; } return ''; }

Integrationstest

På et tidspunkt kommunikerer din kode med en database, et filsystem eller en anden tredjepart. Det kan endda være et andet modul i din app.

Dette stykke implementering skal testes af integrationstests. De har typisk en mere kompliceret opsætning, der involverer forberedelse af testmiljøer, initialisering af afhængigheder osv.

Funktionelle tests

Enhedstest og integrationstest giver dig tillid til, at din app fungerer. Funktionelle tests ser på appen fra brugerens synspunkt og tester, at systemet fungerer som forventet.

I diagrammet ovenfor ser du, at enhedstest udgør den store base i din applikations testpakke. Typisk er de små, der er mange, og de udføres automatisk.

Så lad os nu gå ind i enhedstest lidt mere detaljeret.

Hvorfor skal jeg bryde mig med test af skriveenheder?

Når jeg spørger udviklere, om de skrev tests til deres applikation, fortæller de mig altid: "Jeg havde ikke tid til dem" eller "Jeg har ikke brug for dem, jeg ved, det fungerer."

Så jeg smiler høfligt og fortæller dem, hvad jeg vil fortælle dig. Enhedstest handler ikke kun om testning. De hjælper dig også på andre måder, så du kan:

Vær sikker på at din kode fungerer. Hvornår var sidst, du begik en kodeændring, mislykkedes din build, og halvdelen af ​​din app holdt op med at arbejde? Min var i sidste uge.

Men det er stadig OK. Det virkelige problem er, når bygningen lykkes, ændringen implementeres, og din app begynder at være ustabil.

Når det sker, begynder du at miste tilliden til din kode og til sidst bare bede for, at appen fungerer. Enhedstest hjælper dig med at finde problemer meget hurtigere og få tillid.

Lav bedre arkitektoniske beslutninger. Kodeændringer, men nogle beslutninger om platform, moduler, struktur og andre skal tages i de tidlige faser af et projekt.

Når du begynder at tænke på enhedstest lige i starten, hjælper det dig med at strukturere din kode bedre og opnå korrekt adskillelse af bekymringer. Du vil ikke blive fristet til at tildele flere ansvarsområder til enkeltkodeblokke, da det ville være et mareridt at teste enheden.

Find funktionalitet inden kodning. Du skriver metodens signatur og begynder at implementere den med det samme. Åh, men hvad skal der ske, hvis en parameter er nul? Hvad hvis dens værdi ligger uden for det forventede interval eller indeholder for mange tegn? Kaster du en undtagelse eller returnerer null?

Enhedstest hjælper dig med at opdage alle disse tilfælde. Se på spørgsmålene igen, og du finder ud af, at det er nøjagtigt, hvad der definerer dine enhedstestsager.

Jeg er sikker på, at der er mange flere fordele ved at skrive enhedstest. Det er bare dem, som jeg husker fra min erfaring. Dem, som jeg lærte på den hårde måde.

Sådan skriver du din første JavaScript-enhedstest

Men lad os vende tilbage til JavaScript. Vi starter med Jest, som er en JavaScript-testramme. Det er et værktøj, der muliggør automatisk enhedstest, giver kodedækning og lader os let spotte objekter. Jest har også en udvidelse til Visual Studio Code tilgængelig her.

Der er også andre rammer, hvis du er interesseret, kan du tjekke dem i denne artikel.

npm i jest --save-dev 

Lad os bruge den tidligere nævnte metode getAboutUsLinksom en implementering, vi vil teste:

const englishCode = "en-US"; const spanishCode = "es-ES"; function getAboutUsLink(language){ switch (language.toLowerCase()){ case englishCode.toLowerCase(): return '/about-us'; case spanishCode.toLowerCase(): return '/acerca-de'; } return ''; } module.exports = getAboutUsLink; 

Jeg lagde dette i index.jsfilen. Vi kan skrive tests i den samme fil, men en god praksis er at adskille enhedstests i en dedikeret fil.

De almindelige navngivningsmønstre inkluderer {filename}.test.jsog {filename}.spec.js. Jeg brugte den første index.test.js:

const getAboutUsLink = require("./index"); test("Returns about-us for english language", () => { expect(getAboutUsLink("en-US")).toBe("/about-us"); }); 

Først skal vi importere den funktion, vi vil teste. Hver test defineres som en påkaldelse af testfunktionen. Den første parameter er navnet på testen til din reference. Den anden er en pilfunktion, hvor vi kalder den funktion, vi vil teste, og specificerer hvilket resultat vi forventer. jeg

I dette tilfælde kalder vi getAboutUsLinkfunktion med en-USsom sprogparameter. Vi forventer, at resultatet bliver /about-us.

Nu kan vi installere Jest CLI globalt og køre testen:

npm i jest-cli -g jest 

Hvis du ser en konfigurationsrelateret fejl, skal du sørge for at have din package.jsonfil til stede. Hvis du ikke gør det, skal du oprette en ved hjælp af npm init.

Du skal se noget som dette:

 PASS ./index.test.js √ Returns about-us for english language (4ms) console.log index.js:15 /about-us Test Suites: 1 passed, 1 total Tests: 1 passed, 1 total Snapshots: 0 total Time: 2.389s 

Godt arbejde! Dette var den første enkle JavaScript-enhedstest fra start til slut. Hvis du installerede Visual Studio Code-udvidelsen, kører den automatisk test, når du har gemt en fil. Lad os prøve det ved at udvide testen med denne linje:

expect(getAboutUsLink("cs-CZ")).toBe("/o-nas"); 

Når du har gemt filen, vil Jest informere dig om, at testen mislykkedes. Det hjælper dig med at finde potentielle problemer, selv før du foretager dine ændringer.

Test af avancerede funktionalitets- og mocking-tjenester

I virkeligheden ville sprogkoderne for getAboutUsLink-metoden ikke være konstanter i den samme fil. Deres værdi bruges typisk i hele projektet, så de vil blive defineret i deres eget modul og importeret til alle funktioner, der bruger dem.

import { englishCode, spanishCode } from './LanguageCodes' 

Du kan importere disse konstanter til testen på samme måde. Men situationen bliver mere kompliceret, hvis du arbejder med objekter i stedet for enkle konstanter. Se på denne metode:

import { UserStore } from './UserStore' function getUserDisplayName(){ const user = UserStore.getUser(userId); return `${user.LastName}, ${user.FirstName}`; } 

Denne metode bruger importeret UserStore:

class User { getUser(userId){ // logic to get data from a database } setUser(user){ // logic to store data in a database } } let UserStore = new User(); export { UserStore } 

For korrekt enhedstest af denne metode er vi nødt til at spotte UserStore. En mock er en erstatning for det originale objekt. Det giver os mulighed for at adskille afhængigheder og ægte data fra implementeringen af ​​den testede metode, ligesom dummies hjælper med crashtests af biler i stedet for rigtige mennesker.

Hvis vi ikke brugte mocken, ville vi teste både denne funktion og butikken. Det ville være en integrationstest, og vi bliver sandsynligvis nødt til at spotte den anvendte database.

Spotter en tjeneste

For at spotte objekter kan du enten tilbyde en mocking-funktion eller en manuel mock. Jeg vil fokusere på sidstnævnte, da jeg har en almindelig og enkel brugssag. Men du er velkommen til at tjekke andre spottemuligheder, Jest giver.

jest.mock('./UserStore', () => ({     UserStore: ({         getUser: jest.fn().mockImplementation(arg => ({             FirstName: 'Ondrej',             LastName: 'Polesny'         })), setUser: jest.fn()     }) })); 

Først skal vi specificere, hvad vi håner - ./UserStoremodulet. Dernæst skal vi returnere den mock, der indeholder alle eksporterede objekter fra dette modul.

I denne prøve er det kun det Userobjekt, der er navngivet UserStoremed funktionen getUser. Men med ægte implementeringer kan mocken være meget længere. Alle funktioner, du ikke rigtig er interesseret i inden for enhedstest, kan let hånes med jest.fn().

Enhedstesten for getUserDisplayNamefunktionen svarer til den, vi oprettede før:

test("Returns display name", () => {     expect(getUserDisplayName(1)).toBe("Polesny, Ondrej"); }) 

As soon as I save the file, Jest tells me I have 2 passing tests. If you're executing tests manually, do so now and make sure you see the same result.

Code Coverage Report

Now that we know how to test JavaScript code, it's good to cover as much code as possible with tests. And that is hard to do. In the end, we're just people. We want to get our tasks done and unit tests usually yield an unwanted workload that we tend to overlook. Code coverage is a tool that helps us fight that.

Code coverage will tell you how big a portion of your code is covered by unit tests. Take for example my first unit test checking the getAboutUsLink function:

test("Returns about-us for english language", () => {    expect(getAboutUsLink("en-US")).toBe("/about-us"); }); 

It checks the English link, but the Spanish version stays untested. The code coverage is 50%. The other unit test is checking the getDisplayName function thoroughly and its code coverage is 100%. Together, the total code coverage is 67%. We had 3 use cases to test, but our tests only cover 2 of them.

To see the code coverage report, type the following command into the terminal:

jest --coverage 

Or, if you're using Visual Studio Code with the Jest extension, you can run the command (CTRL+SHIFT+P) Jest: Toggle Coverage Overlay. It will show you right in the implementation which lines of code are not covered with tests.

By running the coverage check, Jest will also create an HTML report. Find it in your project folder under coverage/lcov-report/index.html.

Now, I don't have to mention that you should strive for 100% code coverage, right? :-)

Summary

In this article, I showed you how to start with unit testing in JavaScript. While it's nice to have your code coverage shine at 100% in the report, in reality, it's not always possible to (meaningfully) get there. The goal is to let unit tests help you maintain your code and ensure it always works as intended. They enable you to:

  • clearly define implementation requirements,
  • better design your code and separate concerns,
  • discover issues you may introduce with your newer commits,
  • and give you confidence that your code works.

The best place to start is the Getting started page in the Jest documentation so you can try out these practices for yourself.

Har du din egen erfaring med testkode? Jeg vil meget gerne høre det, lad mig det vide på Twitter eller deltage i en af ​​mine Twitch-streams.