En omfattende guide til integrationstest med fokus på API-test ved hjælp af Supertest, der dækker opsætning, bedste praksis og avancerede teknikker til robust applikationstest.
Integrationstest: Behersk API-test med Supertest
Inden for softwareudvikling er det afgørende at sikre, at individuelle komponenter fungerer korrekt isoleret (enhedstest). Men det er lige så vigtigt at verificere, at disse komponenter fungerer problemfrit sammen. Det er her, integrationstest kommer ind i billedet. Integrationstest fokuserer på at validere interaktionen mellem forskellige moduler eller tjenester i en applikation. Denne artikel dykker ned i integrationstest, med særligt fokus på API-test med Supertest, et kraftfuldt og brugervenligt bibliotek til at teste HTTP-assertions i Node.js.
Hvad er integrationstest?
Integrationstest er en type softwaretest, der kombinerer individuelle softwaremoduler og tester dem som en gruppe. Formålet er at afdække fejl i interaktionen mellem integrerede enheder. I modsætning til enhedstest, der fokuserer på individuelle komponenter, verificerer integrationstest dataflowet og kontrolflowet mellem moduler. Almindelige tilgange til integrationstest inkluderer:
- Top-down integration: Starter med modulerne på højeste niveau og integrerer nedad.
- Bottom-up integration: Starter med modulerne på laveste niveau og integrerer opad.
- Big-bang integration: Integrerer alle moduler samtidigt. Denne tilgang anbefales generelt ikke på grund af vanskeligheden ved at isolere problemer.
- Sandwich integration: En kombination af top-down og bottom-up integration.
I konteksten af API'er indebærer integrationstest at verificere, at forskellige API'er fungerer korrekt sammen, at dataene, der sendes mellem dem, er konsistente, og at det overordnede system fungerer som forventet. Forestil dig for eksempel en e-handelsapplikation med separate API'er til produktstyring, brugergodkendelse og betalingsbehandling. Integrationstest ville sikre, at disse API'er kommunikerer korrekt, så brugerne kan gennemse produkter, logge sikkert ind og gennemføre køb.
Hvorfor er API-integrationstest vigtigt?
API-integrationstest er kritisk af flere grunde:
- Sikrer systemets pålidelighed: Det hjælper med at identificere integrationsproblemer tidligt i udviklingscyklussen og forhindrer uventede fejl i produktionen.
- Validerer dataintegritet: Det verificerer, at data overføres og transformeres korrekt mellem forskellige API'er.
- Forbedrer applikationens ydeevne: Det kan afdække ydeevneflaskehalse relateret til API-interaktioner.
- Forbedrer sikkerheden: Det kan identificere sikkerhedssårbarheder, der opstår som følge af forkert API-integration. For eksempel at sikre korrekt godkendelse og autorisation, når API'er kommunikerer.
- Reducerer udviklingsomkostninger: At rette integrationsproblemer tidligt er betydeligt billigere end at håndtere dem senere i udviklingslivscyklussen.
Overvej en global platform til booking af rejser. API-integrationstest er altafgørende for at sikre problemfri kommunikation mellem API'er, der håndterer flyreservationer, hotelbookinger og betalingsgateways fra forskellige lande. Manglende korrekt integration af disse API'er kan føre til forkerte bookinger, betalingsfejl og en dårlig brugeroplevelse, hvilket negativt påvirker platformens omdømme og omsætning.
Introduktion til Supertest: Et kraftfuldt værktøj til API-test
Supertest er en højniveau-abstraktion til test af HTTP-requests. Det giver en bekvem og flydende API til at sende requests til din applikation og assertere på svarene. Bygget oven på Node.js er Supertest specifikt designet til at teste Node.js HTTP-servere. Det fungerer usædvanligt godt med populære test-frameworks som Jest og Mocha.
Nøglefunktioner i Supertest:
- Let at bruge: Supertest tilbyder en simpel og intuitiv API til at sende HTTP-requests og lave assertions.
- Asynkron test: Det håndterer problemfrit asynkrone operationer, hvilket gør det ideelt til test af API'er, der er afhængige af asynkron logik.
- Flydende interface: Det giver et flydende interface, der giver dig mulighed for at kæde metoder sammen for præcise og læsbare tests.
- Omfattende assertion-support: Det understøtter en bred vifte af assertions til verificering af responsstatuskoder, headers og bodies.
- Integration med test-frameworks: Det integreres problemfrit med populære test-frameworks som Jest og Mocha, så du kan bruge din eksisterende testinfrastruktur.
Opsætning af dit testmiljø
Før vi begynder, lad os opsætte et grundlæggende testmiljø. Vi antager, at du har Node.js og npm (eller yarn) installeret. Vi vil bruge Jest som vores test-framework og Supertest til API-test.
- Opret et Node.js-projekt:
mkdir api-testing-example
cd api-testing-example
npm init -y
- Installer afhængigheder:
npm install --save-dev jest supertest
npm install express # Eller dit foretrukne framework til at oprette API'en
- Konfigurer Jest: Tilføj følgende til din
package.json
-fil:
{
"scripts": {
"test": "jest"
}
}
- Opret et simpelt API-endepunkt: Opret en fil ved navn
app.js
(eller lignende) med følgende kode:
const express = require('express');
const app = express();
const port = 3000;
app.get('/hello', (req, res) => {
res.send('Hello, World!');
});
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`);
});
module.exports = app; // Eksporter til test
Skriv din første Supertest-test
Nu hvor vi har vores miljø opsat, lad os skrive en simpel Supertest-test for at verificere vores API-endepunkt. Opret en fil ved navn app.test.js
(eller lignende) i roden af dit projekt:
const request = require('supertest');
const app = require('./app');
describe('GET /hello', () => {
it('svarer med 200 OK og returnerer "Hello, World!"', async () => {
const response = await request(app).get('/hello');
expect(response.statusCode).toBe(200);
expect(response.text).toBe('Hello, World!');
});
});
Forklaring:
- Vi importerer
supertest
og vores Express-app. - Vi bruger
describe
til at gruppere vores tests. - Vi bruger
it
til at definere en specifik testcase. - Vi bruger
request(app)
til at oprette en Supertest-agent, der vil lave requests til vores app. - Vi bruger
.get('/hello')
til at sende en GET-request til/hello
-endepunktet. - Vi bruger
await
til at vente på svaret. Supertests metoder returnerer promises, hvilket giver os mulighed for at bruge async/await for renere kode. - Vi bruger
expect(response.statusCode).toBe(200)
til at assertere, at responsstatuskoden er 200 OK. - Vi bruger
expect(response.text).toBe('Hello, World!')
til at assertere, at respons-body er "Hello, World!".
For at køre testen, udfør følgende kommando i din terminal:
npm test
Hvis alt er sat korrekt op, skulle du se testen bestå.
Avancerede Supertest-teknikker
Supertest tilbyder en bred vifte af funktioner til avanceret API-test. Lad os udforske nogle af dem.
1. Afsendelse af request-bodies
For at sende data i request-body'en kan du bruge .send()
-metoden. Lad os for eksempel oprette et endepunkt, der accepterer JSON-data:
app.post('/users', express.json(), (req, res) => {
const { name, email } = req.body;
// Simuler oprettelse af en bruger i en database
const user = { id: Date.now(), name, email };
res.status(201).json(user);
});
Her er, hvordan du kan teste dette endepunkt ved hjælp af Supertest:
describe('POST /users', () => {
it('opretter en ny bruger', async () => {
const userData = {
name: 'John Doe',
email: 'john.doe@example.com',
};
const response = await request(app)
.post('/users')
.send(userData)
.expect(201);
expect(response.body).toHaveProperty('id');
expect(response.body.name).toBe(userData.name);
expect(response.body.email).toBe(userData.email);
});
});
Forklaring:
- Vi bruger
.post('/users')
til at sende en POST-request til/users
-endepunktet. - Vi bruger
.send(userData)
til at sendeuserData
-objektet i request-body'en. Supertest sætter automatiskContent-Type
-headeren tilapplication/json
. - Vi bruger
.expect(201)
til at assertere, at responsstatuskoden er 201 Created. - Vi bruger
expect(response.body).toHaveProperty('id')
til at assertere, at respons-body'en indeholder enid
-egenskab. - Vi bruger
expect(response.body.name).toBe(userData.name)
ogexpect(response.body.email).toBe(userData.email)
til at assertere, atname
- ogemail
-egenskaberne i respons-body'en matcher de data, vi sendte i requesten.
2. Indstilling af headers
For at indstille brugerdefinerede headers i dine requests kan du bruge .set()
-metoden. Dette er nyttigt til at indstille godkendelsestokens, content-types eller andre brugerdefinerede headers.
describe('GET /protected', () => {
it('kræver godkendelse', async () => {
const response = await request(app).get('/protected').expect(401);
});
it('returnerer 200 OK med et gyldigt token', async () => {
// Simuler at få et gyldigt token
const token = 'valid-token';
const response = await request(app)
.get('/protected')
.set('Authorization', `Bearer ${token}`)
.expect(200);
expect(response.text).toBe('Protected Resource');
});
});
Forklaring:
- Vi bruger
.set('Authorization', `Bearer ${token}`)
til at indstilleAuthorization
-headeren tilBearer ${token}
.
3. Håndtering af cookies
Supertest kan også håndtere cookies. Du kan indstille cookies ved hjælp af .set('Cookie', ...)
-metoden, eller du kan bruge .cookies
-egenskaben til at tilgå og ændre cookies.
4. Test af filuploads
Supertest kan bruges til at teste API-endepunkter, der håndterer filuploads. Du kan bruge .attach()
-metoden til at vedhæfte filer til requesten.
5. Brug af assertion-biblioteker (Chai)
Selvom Jests indbyggede assertion-bibliotek er tilstrækkeligt i mange tilfælde, kan du også bruge mere kraftfulde assertion-biblioteker som Chai med Supertest. Chai giver en mere udtryksfuld og fleksibel assertion-syntaks. For at bruge Chai skal du installere det:
npm install --save-dev chai
Derefter kan du importere Chai i din testfil og bruge dens assertions:
const request = require('supertest');
const app = require('./app');
const chai = require('chai');
const expect = chai.expect;
describe('GET /hello', () => {
it('svarer med 200 OK og returnerer "Hello, World!"', async () => {
const response = await request(app).get('/hello');
expect(response.statusCode).to.equal(200);
expect(response.text).to.equal('Hello, World!');
});
});
Bemærk: Du skal muligvis konfigurere Jest til at fungere korrekt med Chai. Dette indebærer ofte at tilføje en opsætningsfil, der importerer Chai og konfigurerer det til at fungere med Jests globale expect
.
6. Genbrug af agenter
For tests, der kræver opsætning af et specifikt miljø (f.eks. godkendelse), er det ofte en fordel at genbruge en Supertest-agent. Dette undgår overflødig opsætningskode i hver testcase.
describe('Godkendte API-tests', () => {
let agent;
beforeAll(() => {
agent = request.agent(app); // Opret en vedvarende agent
// Simuler godkendelse
return agent
.post('/login')
.send({ username: 'testuser', password: 'password123' });
});
it('kan tilgå en beskyttet ressource', async () => {
const response = await agent.get('/protected').expect(200);
expect(response.text).toBe('Protected Resource');
});
it('kan udføre andre handlinger, der kræver godkendelse', async () => {
// Udfør andre godkendte handlinger her
});
});
I dette eksempel opretter vi en Supertest-agent i beforeAll
-hooket og godkender agenten. Efterfølgende tests inden for describe
-blokken kan derefter genbruge denne godkendte agent uden at skulle godkende igen for hver test.
Bedste praksis for API-integrationstest med Supertest
For at sikre effektiv API-integrationstest, overvej følgende bedste praksis:
- Test end-to-end arbejdsgange: Fokuser på at teste komplette bruger-workflows i stedet for isolerede API-endepunkter. Dette hjælper med at identificere integrationsproblemer, der måske ikke er synlige, når man tester individuelle API'er isoleret.
- Brug realistiske data: Brug realistiske data i dine tests for at simulere virkelige scenarier. Dette inkluderer brug af gyldige dataformater, grænseværdier og potentielt ugyldige data for at teste fejlhåndtering.
- Isoler dine tests: Sørg for, at dine tests er uafhængige af hinanden, og at de ikke er afhængige af delt tilstand. Dette vil gøre dine tests mere pålidelige og lettere at fejlfinde. Overvej at bruge en dedikeret testdatabase eller at mocke eksterne afhængigheder.
- Mock eksterne afhængigheder: Brug mocking til at isolere din API fra eksterne afhængigheder, såsom databaser, tredjeparts-API'er eller andre tjenester. Dette vil gøre dine tests hurtigere og mere pålidelige, og det vil også give dig mulighed for at teste forskellige scenarier uden at være afhængig af tilgængeligheden af eksterne tjenester. Biblioteker som
nock
er nyttige til at mocke HTTP-requests. - Skriv omfattende tests: Sigt efter omfattende testdækning, herunder positive tests (verificering af vellykkede svar), negative tests (verificering af fejlhåndtering) og grænsetests (verificering af kanttilfælde).
- Automatiser dine tests: Integrer dine API-integrationstests i din continuous integration (CI) pipeline for at sikre, at de køres automatisk, hver gang der foretages ændringer i kodebasen. Dette vil hjælpe med at identificere integrationsproblemer tidligt og forhindre dem i at nå produktionen.
- Dokumenter dine tests: Dokumenter dine API-integrationstests klart og præcist. Dette vil gøre det lettere for andre udviklere at forstå formålet med testene og at vedligeholde dem over tid.
- Brug miljøvariabler: Gem følsomme oplysninger som API-nøgler, databaseadgangskoder og andre konfigurationsværdier i miljøvariabler i stedet for at hardcode dem i dine tests. Dette vil gøre dine tests mere sikre og lettere at konfigurere til forskellige miljøer.
- Overvej API-kontrakter: Anvend API-kontrakttest for at validere, at din API overholder en defineret kontrakt (f.eks. OpenAPI/Swagger). Dette hjælper med at sikre kompatibilitet mellem forskellige tjenester og forhindrer breaking changes. Værktøjer som Pact kan bruges til kontrakttest.
Almindelige fejl, der skal undgås
- Ikke at isolere tests: Tests skal være uafhængige. Undgå at være afhængig af resultatet af andre tests.
- Test af implementeringsdetaljer: Fokuser på API'ens adfærd og kontrakt, ikke dens interne implementering.
- Ignorering af fejlhåndtering: Test grundigt, hvordan din API håndterer ugyldige input, kanttilfælde og uventede fejl.
- At springe godkendelses- og autorisationstest over: Sørg for, at din API's sikkerhedsmekanismer testes korrekt for at forhindre uautoriseret adgang.
Konklusion
API-integrationstest er en essentiel del af softwareudviklingsprocessen. Ved at bruge Supertest kan du nemt skrive omfattende og pålidelige API-integrationstests, der hjælper med at sikre kvaliteten og stabiliteten af din applikation. Husk at fokusere på at teste end-to-end arbejdsgange, bruge realistiske data, isolere dine tests og automatisere din testproces. Ved at følge disse bedste praksisser kan du markant reducere risikoen for integrationsproblemer og levere et mere robust og pålideligt produkt.
Efterhånden som API'er fortsat driver moderne applikationer og microservices-arkitekturer, vil vigtigheden af robust API-test, og især integrationstest, kun fortsætte med at vokse. Supertest giver et kraftfuldt og tilgængeligt værktøjssæt for udviklere over hele verden til at sikre pålideligheden og kvaliteten af deres API-interaktioner.