HTTP Mock Testing i Node.js

Hva du skal skape

HTTP-mock-tester lar deg implementere og teste en funksjon, selv om avhengige tjenester ikke er implementert ennå. Du kan også teste tjenester som allerede er implementert uten at du faktisk ringer disse tjenestene, med andre ord kan du teste dem offline. Du må analysere dine forretningsbehov for det programmet først, og skrive dem som scenarier i designdokumenter.

La oss si at du utvikler en Node.js-klient-API for en medieklient som er en tjeneste (det er ingen slik tjeneste i virkeligheten, bare for testing) for musikk, video, bilder osv. Det vil være mange funksjoner i denne API, og det kan være behov for noen endringer fra tid til annen. 

Hvis du ikke har noen tester for denne API-en, vet du ikke hvilke problemer det vil føre til. Hvis du har tester for denne API-en, kan du imidlertid oppdage problemer ved å kjøre alle testene du har skrevet før. Hvis du utvikler en ny funksjon, må du legge til angitte testtilfeller for det. Du finner en API som allerede er implementert i dette GitHub-depotet. Du kan laste ned prosjektet og kjøre npm test for å kjøre alle testene. La oss fortsette testdelen.

For en HTTP mock test, vil vi bruke nock, som er et HTTP mocking og forventning bibliotek for Node.js. I HTTP mock testing kan du bruke følgende flyt:

  1. Definer en forespørsel / svar mock-regel.
  2. Lag en test og bruk din funksjon i testen.
  3. Sammenlign de forventede resultatene med de faktiske resultatene i test tilbakeringingen.

Enkel testing

La oss tenke på en medieklient-API. La oss si at du ringer til funksjonen musicsList ved å bruke en forekomst av Media bibliotek som nedenfor:

var Media = krever ('... / lib / media'); var mediaClient = ny Media ("your_token_here"); mediaClient.musicsList (funksjon (feil, respons) console.logs (respons);) 

I dette tilfellet får du en musikkliste i respons variabel ved å gjøre en forespørsel til https://ap.example.com/musics inne i denne funksjonen. Hvis du vil skrive for dette, må du tøffe forespørsler for å få testene dine til å kjøre offline. La oss simulere denne forespørselen i nock. 

beskrive ('Music Tests', funksjon () it ('should list music', funksjon (ferdig) nock ('https://api.example.com') .get ('/ musics') .reply (200 , 'OK'); mediaClient.musicList (funksjon (feil, svar) forvente (svar) .to.eql ('OK') gjort ())))

Beskriv ('Musikkprøver', funksjon () ... er for å gruppere tester. I eksempelet ovenfor grupperer vi musikkrelaterte tester under samme gruppe. det ('skal liste musikk', funksjon (ferdig) ... er for testing av spesifikke handlinger i musikkrelaterte funksjoner. I hver test ferdig tilbakeringing er gitt for å sjekke testresultatet i tilbakekallingsfunksjonen til den virkelige funksjonen. I mock-forespørselen antar vi at den vil svare OK hvis vi kaller musicList funksjon. Det forventede og faktiske resultatet sammenlignes inne i tilbakekallingsfunksjonen.

Matchende fra en fil

Du kan definere din forespørsel og svardata i en fil. Du kan se nedenstående eksempel for å samsvare svardata fra en fil.

det ('skal lage musikk og svare som i ressursfil', funksjon (ferdig) nock ('https://api.example.com'). post ('/ musics', title: 'Røyke på vannet' , forfatter: 'Deep Purple', varighet: '5,40 min.') .reply (200, funksjon (uri, requestBody) return fs.createReadStream (path.normalize (__ dirname + '/resources/new_music_response.json' utf8 '))); mediaClient.musicCreate (tittel:' Røk på vannet ', forfatter:' Deep Purple ', varighet:' 5,40 min. ', funksjon (feil, svar) forventer (JSON.parse svar) .musikk.id) .to.eql (3164495) gjort ()))

Når du lager et musikk, må svaret svare til svaret fra filen.

Kjedespørsmål

Du kan også kjede en mock forespørsel i ett omfang som nedenfor:

det ('det burde lage musikk og deretter slette', funksjon (ferdig) nock ('https://api.example.com') .post ('/ musics', title: 'Maps', forfatter: 'Maroon5 'varighet:' 5:00 min. ') .reply (200, musikk: id: 3164494, tittel:' Maps ', forfatter:' Maroon5 ', varighet:' 7:00 min. ') .delete ('/ musics /' + 3164494) .reply (200, 'Music deleted') mediaClient.musicCreate (tittel: 'Maps', forfatter: 'Maroon5', varighet: '5:00 min.' (feil, svar) var musicId = JSON.parse (respons) .musikk.id forventer (musicId) .to.eql (3164494) mediaClient.musicDelete (musicId, funksjon (feil, svar) forvente (svar). eql ('Music deleted') gjort ())))

Som du kan se, kan du gjemme musikkopprettelse og -respons først, og deretter slette musikk, og til slutt svaret på andre data. 

Matchende forespørsler

I medieklienten, Innholdstype: søknad / json er gitt i forespørselhodene. Du kan teste et bestemt overskrift som dette:

det ('skal gi token i header', funksjon (ferdig) nock ('https://api.example.com', reqheaders: 'Content-Type': 'application / json') .get '/ musics') .ply (200, 'OK') mediaClient.musicList (funksjon (feil, svar) forvente (svar) .to.eql ('OK') ferdig ()))

Når du gjør en forespørsel til https://api.example.com/musics med en header Innholdstype: søknad / json, det vil bli oppfanget av Nock HTTP mock ovenfor, og du vil kunne teste det forventede og faktiske resultatet. Du kan også teste svarhodene på samme måte bare ved å angi svarhodet i svardelen:

det ('skal gi spesifikke overskrift som svar', funksjon (ferdig) nock ('https://api.example.com') .get ('/ musics') .reply (200, 'OK', 'Innhold -Type ':' application / json ') mediaClient.musicList (funksjon (feil, svar) forvente (svar) .to.eql (' OK ') gjort ()))

Svar med standardhoder

Du kan angi standard svarhoder for forespørslene i ett omfang ved å bruke defaultReplyHeaders som følger:

den ('skal gi standard respons header', funksjon (ferdig) nock ('https://api.example.com') .defaultReplyHeaders ('Content-Type': 'application / json') .get / music ') .reply (200,' OK, med standard respons headers ') mediaClient.musicList (funksjon (feil, svar) forvente (svar) .to.eql (' OK, med standard respons headers ') ))

Når du ringer til API, finner du standard svarhodene i svaret, selv om det ikke er angitt.

HTTP-operasjoner

HTTP-operasjoner er grunnleggende for klientforespørsler. Du kan fange opp alle HTTP-operasjoner ved å bruke avskjære:

omfang ('http://api.example.com') .intercept ('/ musics / 1', 'DELETE') .reply (404);

Når du prøver å slette musikk med ID 1, svarer det på 404. Du kan sammenligne ditt faktiske resultat med 404 for å sjekke teststatusen. Også du kan bruke , POST, SLETT, LAPP, SETTE, og HODE på samme måten.

Response Repetition

Normalt er mock forespørsler gjort ved hjelp av nock tilgjengelig for første gang. Du kan gjøre det tilgjengelig så mye du vil ha slik:

var scope = nock ('https://api.example.com') .get ('/ musics') .times (2) .reply (200, 'OK med musikkliste'); http.get ( 'https://api.example.com/musics'); // "OK med musikkliste" http.get ('https://apiexample.com/musics'); // "OK med musikkliste" http.get ('https://apiexample.com/musics'); // "Virkelig respons fra api"

Du er begrenset til å gjøre to forespørsler som mock HTTP-forespørsler. Når du gjør tre eller flere forespørsler, vil du automatisk lage en reell forespørsel til API.

Tilpasset port

Du kan også gi en egendefinert port i API-nettadressen din:

det ('skal håndtere spesifikke port', funksjon (ferdig) forespørsel nock ('https://api.example.com:8081') .get ('/') .reply (200, 'OK med tilpasset port') ('https://api.example.com:8081', funksjon (feil, svar, kropp) forventer (kropp) .to.eql ('OK med tilpasset port') gjort ()))

Omfangsfiltrering

Scope-filtrering blir viktig når du vil filtrere domenet, protokollen og porten for testene dine. For eksempel vil underprøven gi deg mulighet til å henge forespørsler som har underdomener som API0, API1, API2 osv.

den ('skal også støtte underdomener', funksjon (ferdig) nock ('http://api.example.com', filteringScope: funksjon (omfang) return / ^ http: \ / \ / api [0- 9] *. Example.com/.test(scope);) .get ('/ musics') .reply (200, 'OK med dynamisk underdomener') forespørsel ('http://api2.example.com/ musics ', funksjon (feil, respons, kropp) forventer (kropp) .to.eql (' OK med dynamiske underdomener ') gjort ()))

Du er ikke begrenset til en nettadresse her; Du kan teste underdomener som er angitt i regexp.

Sti filtrering

Noen ganger kan nettadresseparametrene dine variere. For eksempel er paginasjonsparametrene ikke statiske. I så fall kan du bruke følgende test:

den ('skal støtte dynamisk paginering', funksjon (ferdig) nock ('http://api.example.com') .filtreringPath (/ page = [^ &] * / g, 'page = 123') .get ('/ musics? page = 123') .reply (200, 'Ok respons with paginate') forespørsel ('http://api.example.com/musics?page=13', funksjon (feil, svar, kropp) forvente (kropp) .to.eql ('Ok svar med paginering') ferdig ()))

Be om kroppsfiltrering

Hvis du har varierende felt i din forespørsel, kan du bruke filteringRequestBody å eliminere varierende felt som dette:

det ('skal lage film med dynamisk tittel', funksjon (ferdig) nock ('http://api.example.com') .filteringRequestBody (funksjon (sti) return 'test') .post ('/ musics ',' test ') .reply (201,' OK '); var options = url:' http://api.example.com/musics ', metode:' POST ', body:' author = test_author & title = test ' forespørsel (valg, funksjon (feil, svar, kropp) forventer (kropp) .to.eql (' OK ') gjort ()))

Her forfatter kan variere, men du kan fange opp forespørselslegemet med title = test.

Forespørsel Header Match

I denne klienten brukes et token til å godkjenne klientbrukeren. Du må sjekke overskriften for å se et gyldig token i Autorisasjon Overskrift:

det ('skal matche bærer token header', funksjon (ferdig) nock ('https://api.example.com') .matchHeader ('Authorization', /Bearer.*/) .get ('/ musics') .comment (200, 'Ok svar med musikkliste') mediaClient.musicList (funksjon (feil, svar) forvente (svar) .to.eql ('OK svar med musikkliste') gjort ()))

Slå av eller på

I testtilfeller kan du aktivere ekte HTTP-forespørsler ved å sette inn allowUnmocked som sant. La oss se på følgende tilfelle:

den ('bør be om å utføre "http://api.example.com", funksjon (ferdig) var scope = nock (' http://api.example.com ', allowUnmocked: true) .get ('/ musics') .reply (200, 'OK'); http.get ('http://api.example.com/musics'); // Dette vil bli avlyst av nock http.get ('http: //api.example.com/videos '); // Dette vil gjøre en reell forespørsel til tjeneste)

I et nock-scenario kan du se et scenario for / musikken URI, men også hvis du gjør noen annonse annerledes enn / musikken, Det vil være en reell forespørsel til den angitte webadressen i stedet for en feiltest.

Andre verktøy

Vi har dekket hvordan du skriver scenarier ved å gi en prøveforespørsel, svar, header, etc., og sjekke det faktiske og forventede resultatet. Du kan også bruke noen forventningsverktøy som er ferdig(), fjern rekkevidde scenariet med cleanAll (), bruk et nock-scenario for alltid ved å bruke fortsette(), og listen i venteproblemer i testet ditt ved å bruke pendingMocks ().

er ferdig()

La oss si at du har skrevet et nock-scenario og utfører en ekte funksjon til et testscenario. I testfasen kan du sjekke om det skrevne scenariet er utført eller ikke som følger:

det ('bør be om å bli utført til' http://api.example.com '', funksjon (ferdig) var musicList = nock ('http://api.example.com') .get ('/ musics') .reply (200, 'OK med musikkliste'); forespørsel ('http://api.example.com/musics', funksjon (feil, svar) forventer (musicList.isDone ()) .to.eql ) gjort ()))

Innenfor forespørsel tilbakeringing, forventer vi at nettadressen i nock-scenariet (http://api.example.com/musics) skal ringes. 

cleanAll ()

Hvis du bruker et nock-scenario med fortsette(), Det vil være tilgjengelig for alltid under utførelsen av testen din. Du kan rydde scenariet når du vil, ved å bruke denne kommandoen som vist i følgende eksempel:

den ('skulle mislykkes på grunn av rydding av omfanget', funksjon (ferdig) var musicList = nock ('http://api.example.com') .get ('/ musics') .reply (200, 'OK med musikk liste '); nock.cleanAll (); forespørsel (' http://api.example.com/musics ', funksjon (feil, respons, kropp) forventer (kropp) .not.eql (' OK med musikkliste ' ) gjort ()))

I denne testen samsvarer vår forespørsel og svar med scenariet. Men etterpå nock.cleanAll () Vi tilbakestiller scenariet, og vi forventer ikke et resultat med 'OK med musikkliste'.

fortsette()

Hver nock-scenario er bare tilgjengelig første gang. Hvis du vil gjøre det levende for alltid, kan du bruke en test som denne:

det ('skal være tilgjengelig for uendelig forespørsel', funksjon (ferdig) nock ('http://api.example.com') .get ('/ musics') .reply (200, 'OK') // Først anropsforespørsel ('https://api.example.com/musics', funksjon (feil, svar, kropp) forventer (kropp) .to.eql ('OK') ferdig ()) // Andre anropsforespørsel ( 'https://api.example.com/musics', funksjon (feil, svar, kropp) forventer (kropp) .to.eql ('OK') gjort ()) // ...)

pendingMocks ()

Du kan liste de ventende nock scenariene som ikke er ferdig ennå i testen din som følger:

den ('skal logge på ventende mocks', funksjon (ferdig) var scope = nock ('http://api.example.com') .persist () .get ('/') .reply (200, 'OK' ); hvis (! scope.isDone ()) console.error ('Waiting mocks:% j', scope.pendingMocks ());)

Du vil kanskje også se handlingene som ble utført under testkjøring. Du klarer å gjøre det ved å bruke følgende eksempel:

det ('skal logge alle forespørsler', funksjon (ferdig) var musicList = nock ('http://api.example.com') .log (console.log) .get ('/ musics') .reply ( 200, 'OK med musikkliste'); forespørsel ('http://api.example.com/musics', funksjon (feil, svar, kropp) forventer (kropp) .eql ('OK med musikkliste') ferdig ()))

Denne testen genererer en utgang som vist nedenfor:

som matcher GET http://api.example.com:80/musics for å få http://api.example.com:80/musics: true

Rask tips

Noen ganger må du deaktivere mocking for bestemte nettadresser. For eksempel vil du kanskje ringe den virkelige http://tutsplus.com, i stedet for den spottede. Du kan bruke:

nock.nock.enableNetConnect ( 'tutsplus.com');

Dette vil gjøre en reell forespørsel inne i testene dine. Du kan også aktivere / deaktivere globalt ved å bruke:

nock.enableNetConnect (); nock.disableNetConnect ();

Vær oppmerksom på at hvis du deaktiverer nettverksforbindelse, og prøver å få tilgang til en uhindret nettadresse, får du en NetConnectNotAllowedError unntak i testen din. 

Konklusjon

Det er best praksis å skrive testene dine før virkelig gjennomføring, selv om din avhengige tjeneste ikke er implementert ennå. Utover dette må du kunne teste testene dine når du er offline. Nock lar deg simulere din forespørsel og svar for å teste din søknad.