Kako započeti jedinstveno testiranje vašeg JavaScript koda

Svi znamo da bismo trebali pisati jedinstvene testove. No, teško je znati odakle započeti i koliko vremena posvetiti testovima u usporedbi sa stvarnom provedbom. Pa, gdje početi? I radi li se samo o testiranju koda ili jedinstveni testovi imaju druge prednosti?

U ovom ću članku objasniti različite vrste testova i koje koristi jedinstveno testiranje donosi razvojnim timovima. Izložit ću Jest - okvir za testiranje JavaScript-a.

Različite vrste ispitivanja

Prije nego što uđemo u pojedinosti jedinstvenog testiranja, želim na brzinu pregledati različite vrste testova. Oko njih često nastane neka zbrka i nisam iznenađen. Ponekad je granica između njih prilično tanka.

Jedinstveni testovi

Jedinstveni testovi testiraju samo jedan dio vaše implementacije. Jedinica. Nema ovisnosti ili integracija, nema specifičnosti okvira. Oni su poput metode koja vraća vezu na određenom jeziku:

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

Integracijski testovi

U nekom trenutku vaš kôd komunicira s bazom podataka, datotečnim sustavom ili nekom drugom trećom stranom. To bi mogao biti još jedan modul u vašoj aplikaciji.

Taj bi dio implementacije trebao biti testiran integracijskim testovima. Obično imaju složenije postavke koje uključuju pripremu okruženja za testiranje, inicijaliziranje ovisnosti itd.

Funkcionalni testovi

Jedinstveni testovi i integracijski testovi daju vam sigurnost da vaša aplikacija radi. Funkcionalni testovi gledaju na aplikaciju s gledišta korisnika i testiraju da sustav radi kako se očekivalo.

Na gornjem dijagramu vidite da jedinični testovi čine veliku bazu paketa za testiranje vaše aplikacije. Tipično su mali, ima ih puno i izvršavaju se automatski.

Dakle, krenimo malo detaljnije u jedinične testove.

Zašto bih se gnjavio pisanja jedinstvenih testova?

Kad god pitam programere jesu li napisali testove za svoju aplikaciju, uvijek mi kažu: "Nisam imao vremena za njih" ili "Ne trebaju mi, znam da to djeluje."

Stoga se pristojno nasmiješim i kažem im ono što vam želim reći. Jedinstveni testovi nisu samo ispitivanje. Oni vam pomažu i na druge načine, tako da možete:

Budite sigurni da vaš kod funkcionira. Kada ste zadnji put izvršili promjenu koda, vaša izrada nije uspjela i polovica vaše aplikacije prestala je raditi? Moj je bio prošli tjedan.

Ali to je još uvijek u redu. Pravi je problem kada gradnja uspije, promjena se primijeni i vaša aplikacija počne biti nestabilna.

Kad se to dogodi, počnete gubiti povjerenje u svoj kôd i na kraju se samo molite da aplikacija radi. Jedinstveni testovi pomoći će vam da prije otkrijete probleme i steknete samopouzdanje.

Donosite bolje arhitektonske odluke. Kodeks se mijenja, ali neke odluke o platformi, modulima, strukturi i druge moraju se donijeti tijekom ranih faza projekta.

Kad odmah na početku počnete razmišljati o jedinstvenom testiranju, to će vam pomoći da bolje strukturirate svoj kôd i postignete pravilno razdvajanje problema. Nećete biti u iskušenju dodijeliti višestruke odgovornosti jedinstvenim blokovima koda, jer bi to bilo noćna mora za unit-test.

Prije kodiranja precizno odredite funkcionalnost. Napišete potpis metode i odmah je počnete provoditi. Oh, ali što bi se trebalo dogoditi u slučaju da je parametar null? Što ako je vrijednost izvan očekivanog raspona ili ako sadrži previše znakova? Bacate li iznimku ili vraćate nulu?

Jedinstveni testovi pomoći će vam da otkrijete sve ove slučajeve. Ponovno pogledajte pitanja i vidjet ćete da je upravo to ono što definira vaše jedinice testnih slučajeva.

Siguran sam da ima mnogo više koristi od pisanja jediničnih testova. To su samo oni kojih se sjećam iz svog iskustva. Oni koje sam naučio na teži način.

Kako napisati svoj prvi JavaScript jedinstveni test

No, vratimo se na JavaScript. Započet ćemo s Jestom, koji je okvir za testiranje JavaScript-a. To je alat koji omogućuje automatsko testiranje jedinice, pruža pokrivanje koda i omogućuje nam jednostavno ruganje objektima. Jest ovdje ima i proširenje za Visual Studio Code.

Postoje i drugi okviri, ako ste zainteresirani, možete ih provjeriti u ovom članku.

npm i jest --save-dev 

Upotrijebimo prethodno spomenutu metodu getAboutUsLinkkao implementaciju koju želimo testirati:

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; 

Stavio sam ovo u index.jsdatoteku. Testove možemo pisati u istu datoteku, ali dobra je praksa razdvajanje jediničnih testova u namjensku datoteku.

Uobičajeni obrasci imenovanja uključuju {filename}.test.jsi {filename}.spec.js. Koristio sam prvi index.test.js,:

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

Prvo, moramo uvesti funkciju koju želimo testirati. Svaki test definiran je kao pozivanje testfunkcije. Prvi parametar je naziv testa za vašu referencu. Druga je funkcija strelice gdje pozivamo funkciju koju želimo testirati i odredimo koji rezultat očekujemo. Ja

U ovom slučaju nazivamo getAboutUsLinkfunkciju s en-USkao parametar jezika. Očekujemo da rezultat bude /about-us.

Sada možemo globalno instalirati Jest CLI i pokrenuti test:

npm i jest-cli -g jest 

Ako vidite pogrešku povezanu s konfiguracijom, provjerite imate li package.jsondatoteku. U slučaju da nemate, generirajte jedan pomoću npm init.

Trebali biste vidjeti nešto poput ovoga:

 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 

Odličan posao! Ovo je bio prvi jednostavni JavaScript jedinični test od početka do kraja. Ako ste instalirali ekstenziju Visual Studio Code, on će automatski pokrenuti testove nakon što spremite datoteku. Pokušajmo, produživši test s ovom linijom:

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

Jednom kada spremite datoteku, Jest će vas obavijestiti da test nije uspio. To vam pomaže otkriti potencijalne probleme čak i prije izvršavanja promjena.

Testiranje naprednih usluga i usluga ruganja

U stvarnom životu, jezični kodovi za metodu getAboutUsLink ne bi bili konstante u istoj datoteci. Njihova se vrijednost obično koristi tijekom cijelog projekta, tako da bi se definirale u vlastitom modulu i uvele u sve funkcije koje ih koriste.

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

Te konstante možete uvesti u test na isti način. No situacija će se zakomplicirati ako radite s objektima umjesto s jednostavnim konstantama. Pogledajte ovu metodu:

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

Ova metoda koristi uvezene 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 } 

Da bismo pravilno testirali ovu metodu, moramo se rugati UserStore. Ismijavanje je zamjena za izvorni objekt. Omogućuje nam da odvojimo ovisnosti i stvarne podatke od primjene testirane metode, baš kao što lutke pomažu u testovima sudara automobila umjesto stvarnih ljudi.

Da ne koristimo lažnu sliku, testirali bismo i ovu funkciju i trgovinu. To bi bio test integracije i vjerojatno bismo se trebali rugati korištenoj bazi podataka.

Ruganje usluzi

To mock objects, you can either provide a mocking function or a manual mock. I will focus on the latter as I have a plain and simple use-case. But feel free to check out other mocking possibilities Jest provides.

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

First, we need to specify what are we mocking - the ./UserStore module. Next, we need to return the mock that contains all exported objects from that module.

In this sample, it's only the User object named UserStore with the function getUser. But with real implementations, the mock may be much longer. Any functions you don't really care about in the scope of unit testing can be easily mocked with jest.fn().

The unit test for the getUserDisplayName function is similar to the one we created before:

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.

Do you have your own experience with testing code? I'd love to hear it, let me know on Twitter or join one of my Twitch streams.