example-petstore.com

Domaine d’exemple · Pas un service réel · Les visites depuis un navigateur affichent cette page · Les requêtes API reçoivent 410 Gone

Guide · Tests

Tester avec une API simulée (mock), pas une adresse d’exemple

Un code qui pointe vers une adresse inventée comme api.example-petstore.com envoie malgré tout de vraies requêtes, qui parviennent au propriétaire de ce nom. Une API simulée (mock) offre la même commodité sans ce risque : elle tourne sur votre propre machine ou dans vos tests, répond instantanément et renvoie toujours les données que vous attendez.

Pourquoi pas une adresse d’exemple

  • Une adresse d’exemple qui ressemble à un vrai domaine peut être enregistrée par quelqu’un d’autre. Chaque requête, y compris les en-têtes contenant des clés ou des jetons, arrive alors sur son serveur.
  • Les noms inutilisés sous example.com, comme api.example.com, ne se résolvent pas du tout : le code échoue sur un délai dépassé ou une erreur DNS au lieu de montrer comment il traite de vraies réponses.
  • Les tests qui dépendent d’un serveur public sont lents et instables, et ils cassent dès que ce serveur change.

Une API simulée (mock) règle ces trois problèmes : elle écoute sur localhost ou dans le processus de test, et rien ne quitte votre machine.

Quel outil choisir

OutilIdéal pourFonctionne comme
PrismVous avez une description OpenAPI et voulez des réponses qui la respectentServeur local (Node.js ou Docker), port 4010
WireMockRéponses exactes et enregistrées, cas d’erreur et délais, pour tout langageServeur local (Java ou Docker), port 8080
MSWJavaScript et TypeScript : développement front-end et tests unitairesDans le navigateur ou le processus de test Node.js
json-serverUne API REST fonctionnelle à partir d’un seul fichier JSON, pour les prototypesServeur local (Node.js), port 3000

Pour l’API d’exemple Swagger Petstore elle-même, exécutez l’image officielle en local ; voir Vous cherchez le Swagger Petstore ?

Prism : à partir d’un fichier OpenAPI

Prism lit une description OpenAPI (ou Swagger 2.0) et répond à chacune de ses opérations avec les exemples ou les schémas de ce fichier. Les requêtes qui ne correspondent pas à la description reçoivent une erreur de validation claire, ce qui permet de vérifier un client avant même que la vraie API existe.

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

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

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

L’en-tête Prefer choisit une réponse précise de la description, par exemple un code d’erreur ou un exemple nommé. Avec --dynamic (-d), Prism génère de nouvelles données à partir des schémas à chaque requête.

WireMock : des réponses enregistrées

WireMock répond à partir de fichiers de stubs que vous écrivez ou enregistrez. Il convient à tous les langages et peut simuler des réponses lentes et des pannes qu’un vrai service produit rarement à la demande.

# 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" }
  }
}

# Le démarrer avec le dossier qui contient mappings/ (et __files/ pour les corps plus volumineux)
docker run -it --rm -p 8080:8080 -v "$(pwd)/mocks":/home/wiremock wiremock/wiremock

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

Ajoutez "fixedDelayMilliseconds": 3000 à une réponse pour tester les délais dépassés, ou un statut comme 503 pour tester les nouvelles tentatives. L’API d’administration sur /__admin permet aux tests d’ajouter des stubs et de vérifier quelles requêtes sont arrivées.

MSW et mocks PHP : dans vos tests

Mock Service Worker intercepte les requêtes au sein même de l’application : dans le navigateur via un service worker, dans Node.js au sein du processus de test. Le code testé continue d’appeler son URL de base habituelle ; aucun serveur supplémentaire ne tourne.

// 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 })),
]

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

onUnhandledRequest: 'error' fait échouer un test dès que le code appelle une adresse sans handler : une adresse d’exemple oubliée apparaît ainsi pendant les tests plutôt qu’en production. Dans le navigateur, exécutez une fois npx msw init public/ et démarrez le worker depuis msw/browser.

Dans les tests PHP, le MockHandler de Guzzle fait la même chose au sein du processus de test, et Symfony propose MockHttpClient :

// PHP, Guzzle : réponses dans l’ordre, sans réseau
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/'
);

L’adresse dans les handlers est api.example.com : un nom réservé qui n’atteint jamais un vrai serveur, même si une requête échappe au mock.

json-server : une API REST rapide

json-server transforme un seul fichier JSON en API REST avec des routes pour lister, consulter, créer, modifier et supprimer. Les modifications sont réécrites dans le fichier, ce qui est pratique pour les prototypes et les démos.

# 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"}'

Il n’offre ni validation ni authentification : réservez-le aux prototypes. Pour les tests de contrat, Prism ou WireMock conviennent mieux.

Passer à la vraie API

Gardez l’URL de base dans la configuration, avec le mock comme valeur pour le développement et les tests, et la vraie adresse uniquement là où l’application tourne réellement :

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

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

# production : à définir dans l’environnement d’hébergement, jamais dans le dépôt
API_BASE_URL=https://api.your-real-service.com

Pour aller plus loin, avec une vérification qui empêche les adresses d’exemple d’atteindre la production : Configurer les clients API et les SDK.

Sources