Kuinka aloittaa yksikön testaaminen JavaScript-koodisi

Me kaikki tiedämme, että meidän pitäisi kirjoittaa yksikkötestit. Mutta on vaikea tietää, mistä aloittaa ja kuinka paljon aikaa omistaa testeihin verrattuna varsinaiseen toteutukseen. Joten, mistä aloittaa? Ja onko kyse vain koodin testaamisesta vai onko yksikötesteillä muita etuja?

Tässä artikkelissa selitän erityyppisiä testejä ja mitä etuja yksikötestaus tuo kehitystiimeille. Esittelen Jestin - JavaScript-testauskehyksen.

Erilaisia ​​testauksia

Ennen kuin sukellamme yksikkötestausominaisuuksiin, haluan tehdä nopeasti läpi erilaiset testit. Heidän ympärillään on usein hämmennystä, enkä ole yllättynyt. Joskus niiden välinen viiva on melko ohut.

Yksikkötestit

Yksikkötestit testaa vain yhden osan toteutuksestasi. Yksikkö. Ei riippuvuuksia tai integraatioita, ei kehysominaisuuksia. Ne ovat kuin menetelmä, joka palauttaa linkin tietyllä kielellä:

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

Integraatiotestit

Jossain vaiheessa koodi kommunikoi tietokannan, tiedostojärjestelmän tai muun kolmannen osapuolen kanssa. Se voi olla jopa toinen moduuli sovelluksessasi.

Tuo toteutus on testattava integraatiotesteillä. Niillä on tyypillisesti monimutkaisempi asennus, johon sisältyy testausympäristöjen valmistelu, riippuvuuksien alustaminen ja niin edelleen.

Toiminnalliset testit

Yksikkötestit ja integraatiotestit antavat sinulle varmuuden siitä, että sovelluksesi toimii. Toiminnallisissa testeissä tarkastellaan sovellusta käyttäjän näkökulmasta ja testataan, että järjestelmä toimii odotetulla tavalla.

Yllä olevasta kaaviosta näet, että yksikkötestit muodostavat sovelluksesi testauspaketin suuren pohjan. Tyypillisesti ne ovat pieniä, niitä on paljon, ja ne suoritetaan automaattisesti.

Joten nyt päästään yksikkötesteihin hieman tarkemmin.

Miksi minun pitäisi häiritä kirjoitusyksikötestejä?

Aina kun kysyn kehittäjiltä, ​​ovatko he kirjoittaneet testejä sovellukselleen, he aina sanovat minulle: "Minulla ei ollut aikaa heille" tai "En tarvitse niitä, tiedän, että se toimii".

Joten hymyilen kohteliaasti ja kerron heille, mitä haluan kertoa sinulle. Yksikkötestit eivät ole vain testausta. Ne auttavat sinua myös muilla tavoin, joten voit:

Ole varma, että koodisi toimii. Milloin viimeksi teit koodimuutoksen, koontisi epäonnistui ja puolet sovelluksestasi lakkasi toimimasta? Minun oli viime viikolla.

Mutta se on silti ok. Todellinen ongelma on, kun koontiversio onnistuu, muutos otetaan käyttöön ja sovelluksesi alkaa olla epävakaa.

Kun näin tapahtuu, alat menettää luottamustasi koodiin ja lopulta vain rukoilla, että sovellus toimii. Yksikkötestit auttavat sinua löytämään ongelmat paljon nopeammin ja saavuttamaan itseluottamusta.

Tee parempia arkkitehtonisia päätöksiä. Koodi muuttuu, mutta jotkut päätökset alustasta, moduuleista, rakenteesta ja muista on tehtävä projektin alkuvaiheessa.

Kun alat ajatella yksikkötestausta heti alussa, se auttaa sinua jäsentämään koodisi paremmin ja huolenaiheiden asianmukaisen erottamisen. Sinulla ei ole houkutusta osoittaa useita vastuita yksittäisille koodilohkoille, koska ne olisivat painajaisia ​​yksikötestissä.

Tarkka toiminnallisuus ennen koodausta. Kirjoitat menetelmän allekirjoituksen ja aloitat sen toteuttamisen heti. Voi, mutta mitä pitäisi tapahtua, jos parametri on nolla? Entä jos sen arvo on odotetun alueen ulkopuolella tai sisältää liian monta merkkiä? Heitätkö poikkeuksen vai palaatko tyhjäksi?

Yksikkötestit auttavat sinua löytämään kaikki nämä tapaukset. Katsokaa kysymyksiä uudelleen ja huomaat, että juuri se määrittelee yksikön testitapaukset.

Olen varma, että yksikötestien kirjoittamisessa on paljon enemmän etuja. Nämä ovat vain ne, jotka muistan kokemuksestani. Ne, jotka opin kovalla tavalla.

Ensimmäisen JavaScript-yksikötestin kirjoittaminen

Mutta palataan takaisin JavaScriptiin. Aloitamme Jestillä, joka on JavaScript-testauskehys. Se on työkalu, joka mahdollistaa yksiköiden automaattisen testauksen, tarjoaa koodikattavuuden ja antaa meidän pilkata esineitä helposti. Jestillä on myös Visual Studio -koodin laajennus, joka on saatavana täältä.

On myös muita kehyksiä, jos olet kiinnostunut, voit tarkistaa ne tästä artikkelista.

npm i jest --save-dev 

Käytetään aiemmin mainittua menetelmää getAboutUsLinktestattavana toteutuksena:

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; 

Laitoin tämän index.jstiedostoon. Voimme kirjoittaa testit samaan tiedostoon, mutta hyvä käytäntö on erottaa yksikkötestit erilliseksi tiedostoksi.

Yleisiä nimeämismalleja ovat {filename}.test.jsja {filename}.spec.js. Käytin ensimmäistä index.test.js:

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

Ensinnäkin meidän on tuotava testattava toiminto. Jokainen testi määritellään testfunktion kutsuksi . Ensimmäinen parametri on testin nimi viitteellesi. Toinen on nuolitoiminto, jossa kutsumme testattavan funktion ja määritämme odotettavan tuloksen. Minä

Tässä tapauksessa kutsumme getAboutUsLinkfunktiota en-USkieliparametrina. Odotamme tuloksen olevan /about-us.

Nyt voimme asentaa Jest CLI: n maailmanlaajuisesti ja suorittaa testin:

npm i jest-cli -g jest 

Jos näet kokoonpanoon liittyvän virheen, varmista, että package.jsontiedostosi on läsnä. Jos et, luo yksi käyttämällä npm init.

Sinun pitäisi nähdä jotain tällaista:

 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 

Great job! This was the first simple JavaScript unit test from start to end. If you installed the Visual Studio Code extension, it will run tests automatically once you save a file. Let's try it by extending the test with this line:

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

Once you save the file, Jest will inform you that the test failed. That helps you discover potential issues even before committing your changes.

Testing Advanced Functionality and Mocking Services

In real life, the language codes for the getAboutUsLink method would not be constants in the same file. Their value is typically used throughout the project so they would be defined in their own module and imported into all functions that use them.

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

You can import these constants into the test the same way. But the situation will get more complicated if you're working with objects instead of simple constants. Take a look at this method:

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

This method uses imported 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 } 

In order to properly unit test this method, we need to mock UserStore. A mock is a substitute for the original object. It allows us to separate dependencies and real data from the tested method's implementation just like dummies help with crash tests of cars instead of real people.

If we didn't use the mock, we'd be testing both this function and the store. That would be an integration test and we would likely need to mock the used database.

Mocking a Service

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.

Onko sinulla omaa kokemusta koodin testaamisesta? Haluaisin kuulla sen, kerro minulle Twitterissä tai liity johonkin Twitch-streamistani.