example-petstore.com

Voorbeelddomein · Geen echte dienst · Bezoekers met een browser zien deze pagina · API-verzoeken krijgen 410 Gone

Gids · Testen

Test tegen een mock-API, niet tegen een voorbeeldadres

Code die naar een verzonnen adres zoals api.example-petstore.com wijst, verstuurt toch echte verzoeken, en die komen terecht bij wie die naam bezit. Een mock-API biedt hetzelfde gemak zonder dat risico: hij draait op je eigen computer of in je tests, antwoordt meteen en geeft altijd de gegevens terug die je verwacht.

Waarom geen voorbeeldadres

  • Een voorbeeldadres dat op een echt domein lijkt, kan door iemand anders geregistreerd zijn. Elk verzoek, ook de headers met sleutels of tokens, komt dan op diens server terecht.
  • Ongebruikte namen onder example.com, zoals api.example.com, resolven helemaal niet, dus de code faalt met een time-out of DNS-fout in plaats van te laten zien hoe ze met echte antwoorden omgaat.
  • Tests die van een openbare server afhangen, zijn traag en onbetrouwbaar, en ze breken zodra die server verandert.

Een mock-API lost alle drie op: hij luistert op localhost of binnen het testproces, en er verlaat niets je computer.

Welke tool wanneer

ToolHet meest geschikt voorDraait als
PrismJe hebt een OpenAPI-beschrijving en wilt antwoorden die zich daaraan houdenLokale server (Node.js of Docker), poort 4010
WireMockExacte, vastgelegde antwoorden, foutgevallen en vertragingen, voor elke taalLokale server (Java of Docker), poort 8080
MSWJavaScript en TypeScript: front-endontwikkeling en unittestsIn de browser of in het testproces van Node.js
json-serverEen werkende REST-API uit één JSON-bestand, voor prototypesLokale server (Node.js), poort 3000

Voor de voorbeeld-API Swagger Petstore zelf draai je de officiële image lokaal; zie Op zoek naar de Swagger Petstore?

Prism: vanuit een OpenAPI-bestand

Prism leest een OpenAPI-beschrijving (of Swagger 2.0) en beantwoordt elke operatie daarin met de voorbeelden of schema’s uit dat bestand. Verzoeken die niet bij de beschrijving passen, krijgen een duidelijke validatiefout. Zo kun je een client al controleren voordat de echte API bestaat.

# Met Node.js
npm install -g @stoplight/prism-cli
prism mock openapi.yaml

# Met Docker
docker run --init --rm -v "$(pwd)":/tmp -p 4010:4010 stoplight/prism:5 mock -h 0.0.0.0 /tmp/openapi.yaml

# Daarna
curl http://127.0.0.1:4010/pets
curl http://127.0.0.1:4010/pets/1 -H "Prefer: code=404"

Met de header Prefer kies je een bepaald antwoord uit de beschrijving, zoals een foutcode of een voorbeeld met een naam. Met --dynamic (-d) maakt Prism bij elk verzoek nieuwe gegevens op basis van de schema’s.

WireMock: vastgelegde antwoorden

WireMock antwoordt vanuit stubbestanden die je zelf schrijft of opneemt. Het past bij elke programmeertaal en kan trage antwoorden en storingen nabootsen die een echte dienst zelden op bestelling geeft.

# mocks/mappings/pet.json
{
  "request":  { "method": "GET", "url": "/v1/pets/1" },
  "response": {
    "status": 200,
    "headers": { "Content-Type": "application/json" },
    "jsonBody": { "id": 1, "name": "Rex", "status": "available" }
  }
}

# Start met de map die mappings/ bevat (en __files/ voor grotere bodies)
docker run -it --rm -p 8080:8080 -v "$(pwd)/mocks":/home/wiremock wiremock/wiremock

curl http://localhost:8080/v1/pets/1

Voeg "fixedDelayMilliseconds": 3000 aan een antwoord toe om time-outs te testen, of een status zoals 503 om nieuwe pogingen te testen. Via de beheer-API op /__admin kunnen tests stubs toevoegen en nagaan welke verzoeken zijn binnengekomen.

MSW en PHP-mocks: binnen je tests

Mock Service Worker onderschept verzoeken binnen de applicatie zelf: in de browser via een service worker, in Node.js binnen het testproces. De geteste code blijft gewoon haar normale basis-URL aanroepen; er draait geen extra server.

// handlers.js
import { http, HttpResponse } from 'msw'

export const handlers = [
  http.get('https://api.example.com/v1/pets/:id', ({ params }) =>
    HttpResponse.json({ id: Number(params.id), name: 'Rex', status: 'available' })),
  http.post('https://api.example.com/v1/pets', () =>
    HttpResponse.json({ error: 'name is required' }, { status: 400 })),
]

// In tests (Node.js)
import { setupServer } from 'msw/node'
const server = setupServer(...handlers)
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }))
afterEach(() => server.resetHandlers())
afterAll(() => server.close())

Met onUnhandledRequest: 'error' faalt een test zodra code een adres zonder handler aanroept. Zo duikt een vergeten voorbeeldadres op in de testrun in plaats van in productie. Voer in de browser eenmalig npx msw init public/ uit en start de worker vanuit msw/browser.

In PHP-tests doet de MockHandler van Guzzle hetzelfde binnen het testproces, en Symfony heeft MockHttpClient:

// PHP, Guzzle: antwoorden op volgorde, zonder netwerk
use GuzzleHttp\Client;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Response;

$mock = new MockHandler([
    new Response(200, ['Content-Type' => 'application/json'], '{"id": 1, "name": "Rex", "status": "available"}'),
    new Response(400, [], '{"error": "name is required"}'),
]);
$client = new Client(['handler' => HandlerStack::create($mock), 'base_uri' => 'https://api.example.com/v1/']);

// PHP, Symfony
$client = new Symfony\Component\HttpClient\MockHttpClient(
    [new Symfony\Component\HttpClient\Response\MockResponse('{"id": 1, "name": "Rex"}')],
    'https://api.example.com/v1/'
);

Het adres in de handlers is api.example.com: een gereserveerde naam die nooit een echte server bereikt, ook niet als een verzoek toch langs de mock glipt.

json-server: snel een REST-API

json-server maakt van één JSON-bestand een REST-API met routes om op te sommen, op te vragen, aan te maken, bij te werken en te verwijderen. Wijzigingen worden terug in het bestand geschreven, wat handig is voor prototypes en demo’s.

# db.json
{
  "pets":   [ { "id": "1", "name": "Rex", "status": "available" } ],
  "orders": []
}

npx json-server db.json

curl http://localhost:3000/pets
curl -X POST http://localhost:3000/orders -H "Content-Type: application/json" -d '{"petId": "1"}'

Er is geen validatie of authenticatie, dus hou het bij prototypes; voor contracttests passen Prism of WireMock beter.

Overstappen op de echte API

Zet de basis-URL in de configuratie, met de mock als waarde voor ontwikkeling en tests, en het echte adres alleen daar waar de applicatie echt draait:

# .env.development
API_BASE_URL=http://localhost:4010

# .env.test
API_BASE_URL=http://localhost:8080

# productie: instellen in de hostingomgeving, nooit in de repository
API_BASE_URL=https://api.your-real-service.com

Meer hierover, met een controle die voorkomt dat voorbeeldadressen in productie belanden: API-clients en SDK’s configureren.

Bronnen