Celovit vodnik po integracijskem testiranju z osredotočenostjo na testiranje API-jev s Supertestom, ki zajema nastavitev, najboljše prakse in napredne tehnike za robustno testiranje aplikacij.
Integracijsko testiranje: obvladovanje testiranja API-jev s Supertestom
Na področju razvoja programske opreme je ključnega pomena zagotoviti, da posamezne komponente delujejo pravilno v osami (enotno testiranje). Vendar je enako pomembno preveriti, ali te komponente brezhibno delujejo skupaj. Tu nastopi integracijsko testiranje. Integracijsko testiranje se osredotoča na preverjanje interakcije med različnimi moduli ali storitvami znotraj aplikacije. Ta članek se poglobi v integracijsko testiranje, s posebnim poudarkom na testiranju API-jev s Supertestom, zmogljivo in uporabniku prijazno knjižnico za testiranje HTTP trditev v Node.js.
Kaj je integracijsko testiranje?
Integracijsko testiranje je vrsta testiranja programske opreme, ki združuje posamezne module programske opreme in jih testira kot skupino. Njegov cilj je odkriti napake v interakcijah med integriranimi enotami. Za razliko od enotnega testiranja, ki se osredotoča na posamezne komponente, integracijsko testiranje preverja pretok podatkov in nadzora med moduli. Pogosti pristopi k integracijskemu testiranju vključujejo:
- Integracija od zgoraj navzdol (Top-down): Začne se z moduli na najvišji ravni in se integrira navzdol.
- Integracija od spodaj navzgor (Bottom-up): Začne se z moduli na najnižji ravni in se integrira navzgor.
- Integracija »velikega poka« (Big-bang): Vsi moduli se integrirajo hkrati. Ta pristop je na splošno manj priporočljiv zaradi težav pri izolaciji težav.
- Sendvič integracija: Kombinacija integracije od zgoraj navzdol in od spodaj navzgor.
V kontekstu API-jev integracijsko testiranje vključuje preverjanje, ali različni API-ji pravilno delujejo skupaj, ali so podatki, ki se prenašajo med njimi, skladni in ali celoten sistem deluje, kot je pričakovano. Predstavljajte si na primer aplikacijo za e-trgovino z ločenimi API-ji za upravljanje izdelkov, avtentikacijo uporabnikov in obdelavo plačil. Integracijsko testiranje bi zagotovilo, da ti API-ji pravilno komunicirajo, kar uporabnikom omogoča brskanje po izdelkih, varno prijavo in dokončanje nakupov.
Zakaj je integracijsko testiranje API-jev pomembno?
Integracijsko testiranje API-jev je ključnega pomena iz več razlogov:
- Zagotavlja zanesljivost sistema: Pomaga odkriti težave z integracijo zgodaj v razvojnem ciklu, kar preprečuje nepričakovane napake v produkciji.
- Preverja celovitost podatkov: Preverja, ali se podatki pravilno prenašajo in transformirajo med različnimi API-ji.
- Izboljšuje zmogljivost aplikacije: Lahko odkrije ozka grla v zmogljivosti, povezana z interakcijami API-jev.
- Povečuje varnost: Lahko identificira varnostne ranljivosti, ki izhajajo iz nepravilne integracije API-jev. Na primer, zagotavljanje ustrezne avtentikacije in avtorizacije pri komunikaciji med API-ji.
- Zmanjšuje razvojne stroške: Odpravljanje težav z integracijo zgodaj je bistveno cenejše kot njihovo reševanje kasneje v razvojnem življenjskem ciklu.
Razmislite o globalni platformi za rezervacije potovanj. Integracijsko testiranje API-jev je ključnega pomena za zagotavljanje nemotene komunikacije med API-ji, ki obravnavajo rezervacije letov, hotelske rezervacije in plačilne prehode iz različnih držav. Neuspešna integracija teh API-jev bi lahko povzročila napačne rezervacije, neuspešna plačila in slabo uporabniško izkušnjo, kar bi negativno vplivalo na ugled in prihodke platforme.
Predstavljamo Supertest: zmogljivo orodje za testiranje API-jev
Supertest je visokonivojska abstrakcija za testiranje HTTP zahtevkov. Zagotavlja priročen in tekoč API za pošiljanje zahtevkov vaši aplikaciji in preverjanje odgovorov. Supertest, zgrajen na vrhu Node.js, je posebej zasnovan za testiranje HTTP strežnikov Node.js. Izjemno dobro deluje s priljubljenimi testnimi ogrodji, kot sta Jest in Mocha.
Ključne značilnosti Supertesta:
- Enostaven za uporabo: Supertest ponuja preprost in intuitiven API za pošiljanje HTTP zahtevkov in izvajanje trditev.
- Asinhrono testiranje: Brezhibno obravnava asinhrone operacije, zaradi česar je idealen za testiranje API-jev, ki temeljijo na asinhroni logiki.
- Tekoč vmesnik (Fluent Interface): Zagotavlja tekoč vmesnik, ki omogoča veriženje metod za jedrnate in berljive teste.
- Celovita podpora za trditve: Podpira širok nabor trditev za preverjanje statusnih kod, glav in teles odgovorov.
- Integracija s testnimi ogrodji: Brezhibno se integrira s priljubljenimi testnimi ogrodji, kot sta Jest in Mocha, kar vam omogoča uporabo obstoječe testne infrastrukture.
Nastavitev testnega okolja
Preden začnemo, nastavimo osnovno testno okolje. Predvidevali bomo, da imate nameščen Node.js in npm (ali yarn). Kot testno ogrodje bomo uporabili Jest, za testiranje API-jev pa Supertest.
- Ustvarite Node.js projekt:
mkdir api-testing-example
cd api-testing-example
npm init -y
- Namestite odvisnosti:
npm install --save-dev jest supertest
npm install express # Ali vaše priljubljeno ogrodje za ustvarjanje API-ja
- Konfigurirajte Jest: V datoteko
package.json
dodajte naslednje:
{
"scripts": {
"test": "jest"
}
}
- Ustvarite preprosto končno točko API-ja: Ustvarite datoteko z imenom
app.js
(ali podobno) z naslednjo kodo:
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; // Izvoz za testiranje
Pisanje prvega Supertest testa
Zdaj, ko imamo nastavljeno okolje, napišimo preprost Supertest test za preverjanje naše končne točke API-ja. V korenski mapi projekta ustvarite datoteko z imenom app.test.js
(ali podobno):
const request = require('supertest');
const app = require('./app');
describe('GET /hello', () => {
it('responds with 200 OK and returns "Hello, World!"', async () => {
const response = await request(app).get('/hello');
expect(response.statusCode).toBe(200);
expect(response.text).toBe('Hello, World!');
});
});
Razlaga:
- Uvozimo
supertest
in našo Express aplikacijo. - Uporabimo
describe
za združevanje naših testov. - Uporabimo
it
za opredelitev določenega testnega primera. - Uporabimo
request(app)
za ustvarjanje Supertest agenta, ki bo pošiljal zahtevke naši aplikaciji. - Uporabimo
.get('/hello')
za pošiljanje GET zahtevka na končno točko/hello
. - Uporabimo
await
za čakanje na odgovor. Metode Supertesta vračajo obljube (promises), kar nam omogoča uporabo async/await za čistejšo kodo. - Uporabimo
expect(response.statusCode).toBe(200)
za trditev, da je statusna koda odgovora 200 OK. - Uporabimo
expect(response.text).toBe('Hello, World!')
za trditev, da je telo odgovora »Hello, World!«.
Za zagon testa v terminalu zaženite naslednji ukaz:
npm test
Če je vse pravilno nastavljeno, bi morali videti, da je test uspešno opravljen.
Napredne tehnike Supertesta
Supertest ponuja širok nabor funkcij za napredno testiranje API-jev. Raziščimo nekatere izmed njih.
1. Pošiljanje teles zahtevkov
Za pošiljanje podatkov v telesu zahtevka lahko uporabite metodo .send()
. Na primer, ustvarimo končno točko, ki sprejema JSON podatke:
app.post('/users', express.json(), (req, res) => {
const { name, email } = req.body;
// Simulacija ustvarjanja uporabnika v bazi podatkov
const user = { id: Date.now(), name, email };
res.status(201).json(user);
});
Tako lahko testirate to končno točko s Supertestom:
describe('POST /users', () => {
it('creates a new user', 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);
});
});
Razlaga:
- Uporabimo
.post('/users')
za pošiljanje POST zahtevka na končno točko/users
. - Uporabimo
.send(userData)
za pošiljanje objektauserData
v telesu zahtevka. Supertest samodejno nastavi glavoContent-Type
naapplication/json
. - Uporabimo
.expect(201)
za trditev, da je statusna koda odgovora 201 Created. - Uporabimo
expect(response.body).toHaveProperty('id')
za trditev, da telo odgovora vsebuje lastnostid
. - Uporabimo
expect(response.body.name).toBe(userData.name)
inexpect(response.body.email).toBe(userData.email)
za trditev, da se lastnostiname
inemail
v telesu odgovora ujemata s podatki, ki smo jih poslali v zahtevku.
2. Nastavljanje glav
Za nastavitev glav po meri v vaših zahtevkih lahko uporabite metodo .set()
. To je uporabno za nastavljanje avtentikacijskih žetonov, vrst vsebine ali drugih glav po meri.
describe('GET /protected', () => {
it('requires authentication', async () => {
const response = await request(app).get('/protected').expect(401);
});
it('returns 200 OK with a valid token', async () => {
// Simulacija pridobivanja veljavnega žetona
const token = 'valid-token';
const response = await request(app)
.get('/protected')
.set('Authorization', `Bearer ${token}`)
.expect(200);
expect(response.text).toBe('Protected Resource');
});
});
Razlaga:
- Uporabimo
.set('Authorization', `Bearer ${token}`)
za nastavitev glaveAuthorization
naBearer ${token}
.
3. Upravljanje piškotkov
Supertest lahko upravlja tudi piškotke. Piškotke lahko nastavite z metodo .set('Cookie', ...)
ali pa uporabite lastnost .cookies
za dostop in spreminjanje piškotkov.
4. Testiranje nalaganja datotek
Supertest se lahko uporablja za testiranje končnih točk API-ja, ki obravnavajo nalaganje datotek. Za pripenjanje datotek k zahtevku lahko uporabite metodo .attach()
.
5. Uporaba knjižnic za preverjanje (Chai)
Čeprav je vgrajena knjižnica za trditve v Jestu zadostna za mnoge primere, lahko s Supertestom uporabite tudi močnejše knjižnice za trditve, kot je Chai. Chai ponuja bolj izrazno in prilagodljivo sintakso za trditve. Za uporabo Chai-a ga morate namestiti:
npm install --save-dev chai
Nato lahko Chai uvozite v svojo testno datoteko in uporabite njegove trditve:
const request = require('supertest');
const app = require('./app');
const chai = require('chai');
const expect = chai.expect;
describe('GET /hello', () => {
it('responds with 200 OK and returns "Hello, World!"', async () => {
const response = await request(app).get('/hello');
expect(response.statusCode).to.equal(200);
expect(response.text).to.equal('Hello, World!');
});
});
Opomba: Morda boste morali konfigurirati Jest za pravilno delovanje s Chai-em. To pogosto vključuje dodajanje nastavitvene datoteke, ki uvozi Chai in ga konfigurira za delo z globalnim expect
v Jestu.
6. Ponovna uporaba agentov
Za teste, ki zahtevajo nastavitev določenega okolja (npr. avtentikacija), je pogosto koristno ponovno uporabiti agenta Supertest. S tem se izognete odvečni kodi za nastavitev v vsakem testnem primeru.
describe('Authenticated API Tests', () => {
let agent;
beforeAll(() => {
agent = request.agent(app); // Ustvarite trajnega agenta
// Simulacija avtentikacije
return agent
.post('/login')
.send({ username: 'testuser', password: 'password123' });
});
it('can access a protected resource', async () => {
const response = await agent.get('/protected').expect(200);
expect(response.text).toBe('Protected Resource');
});
it('can perform other actions that require authentication', async () => {
// Tukaj izvedite druga avtenticirana dejanja
});
});
V tem primeru ustvarimo agenta Supertest v kljuki beforeAll
in ga avtenticiramo. Naslednji testi znotraj bloka describe
lahko nato ponovno uporabijo tega avtenticiranega agenta, ne da bi se morali za vsak test ponovno avtenticirati.
Najboljše prakse za integracijsko testiranje API-jev s Supertestom
Za zagotovitev učinkovitega integracijskega testiranja API-jev upoštevajte naslednje najboljše prakse:
- Testirajte celotne delovne tokove: Osredotočite se na testiranje celotnih uporabniških delovnih tokov namesto osamljenih končnih točk API-ja. To pomaga odkriti težave z integracijo, ki morda niso očitne pri testiranju posameznih API-jev v osami.
- Uporabite realistične podatke: V testih uporabite realistične podatke za simulacijo resničnih scenarijev. To vključuje uporabo veljavnih formatov podatkov, mejnih vrednosti in potencialno neveljavnih podatkov za testiranje obravnavanja napak.
- Izolirajte svoje teste: Zagotovite, da so vaši testi med seboj neodvisni in da se ne zanašajo na skupno stanje. To bo naredilo vaše teste bolj zanesljive in lažje za odpravljanje napak. Razmislite o uporabi namenske testne baze podatkov ali posnemanju zunanjih odvisnosti.
- Posnemajte zunanje odvisnosti: Uporabite posnemanje (mocking), da izolirate svoj API od zunanjih odvisnosti, kot so baze podatkov, API-ji tretjih oseb ali druge storitve. To bo naredilo vaše teste hitrejše in bolj zanesljive, hkrati pa vam bo omogočilo testiranje različnih scenarijev brez zanašanja na razpoložljivost zunanjih storitev. Knjižnice, kot je
nock
, so uporabne za posnemanje HTTP zahtevkov. - Pišite celovite teste: Prizadevajte si za celovito pokritost testov, vključno s pozitivnimi testi (preverjanje uspešnih odgovorov), negativnimi testi (preverjanje obravnavanja napak) in mejnimi testi (preverjanje robnih primerov).
- Avtomatizirajte svoje teste: Integrirajte svoje integracijske teste API-jev v svoj cevovod za neprekinjeno integracijo (CI), da zagotovite, da se samodejno izvajajo ob vsaki spremembi kodne baze. To bo pomagalo odkriti težave z integracijo zgodaj in preprečiti, da bi prišle v produkcijo.
- Dokumentirajte svoje teste: Jasno in jedrnato dokumentirajte svoje integracijske teste API-jev. To bo drugim razvijalcem olajšalo razumevanje namena testov in njihovo vzdrževanje skozi čas.
- Uporabite okoljske spremenljivke: Občutljive informacije, kot so ključi API-jev, gesla za baze podatkov in druge konfiguracijske vrednosti, shranjujte v okoljskih spremenljivkah, namesto da bi jih trdo kodirali v testih. To bo naredilo vaše teste bolj varne in lažje za konfiguracijo za različna okolja.
- Upoštevajte pogodbe API-jev: Uporabite testiranje pogodb API-jev (API contract testing) za preverjanje, ali se vaš API drži določene pogodbe (npr. OpenAPI/Swagger). To pomaga zagotoviti združljivost med različnimi storitvami in preprečuje lomljive spremembe. Orodja, kot je Pact, se lahko uporabljajo za testiranje pogodb.
Pogoste napake, ki se jim je treba izogibati
- Neizoliranje testov: Testi morajo biti neodvisni. Izogibajte se zanašanju na izid drugih testov.
- Testiranje podrobnosti implementacije: Osredotočite se na obnašanje in pogodbo API-ja, ne na njegovo notranjo implementacijo.
- Ignoriranje obravnavanja napak: Temeljito preizkusite, kako vaš API obravnava neveljavne vnose, robne primere in nepričakovane napake.
- Preskakovanje testiranja avtentikacije in avtorizacije: Zagotovite, da so varnostni mehanizmi vašega API-ja pravilno preizkušeni, da preprečite nepooblaščen dostop.
Zaključek
Integracijsko testiranje API-jev je bistven del procesa razvoja programske opreme. Z uporabo Supertesta lahko enostavno pišete celovite in zanesljive integracijske teste API-jev, ki pomagajo zagotoviti kakovost in stabilnost vaše aplikacije. Ne pozabite se osredotočiti na testiranje celotnih delovnih tokov, uporabo realističnih podatkov, izolacijo testov in avtomatizacijo testnega procesa. Z upoštevanjem teh najboljših praks lahko bistveno zmanjšate tveganje za težave z integracijo in zagotovite bolj robusten in zanesljiv izdelek.
Ker API-ji še naprej poganjajo sodobne aplikacije in arhitekture mikrostoritev, bo pomen robustnega testiranja API-jev, zlasti integracijskega testiranja, le še naraščal. Supertest razvijalcem po vsem svetu ponuja zmogljiv in dostopen nabor orodij za zagotavljanje zanesljivosti in kakovosti njihovih interakcij z API-ji.