Les tests UI sont précieux, mais ils ne montrent que ce que voit l'utilisateur. L'API est là où vivent la plupart des bugs : la logique backend, la validation des données, les règles d'authentification, les cas limites que l'UI n'expose jamais. Playwright dispose d'un client HTTP intégré qui permet d'écrire des tests API sans installer le moindre outil supplémentaire.
Pourquoi les tests API ont leur place dans votre suite Playwright
L'argument pour les tests API n'est pas de remplacer les tests UI. C'est de tester les bonnes choses au bon niveau.
Un test UI qui crée un élément de voyage passe par le navigateur. Il rend le formulaire, remplit les champs, clique sur Envoyer, attend la mise à jour de la page, puis vérifie le résultat. Ça prend 3 à 5 secondes et peut échouer pour une douzaine de raisons sans rapport avec la fonctionnalité testée : une animation lente, un locateur modifié, une requête réseau instable.
Un test API qui crée le même élément envoie une requête HTTP et vérifie la réponse. Ça prend 50 à 200 millisecondes et échoue uniquement quand l'API elle-même est cassée.
Utilisez les tests API pour la validation des données, les règles d'authentification, les réponses d'erreur, et la logique métier backend. Utilisez les tests UI pour : les parcours utilisateur, le rendu visuel, les interactions frontend. Les deux ont leur place. L'erreur est d'utiliser uniquement des tests UI quand le bug est dans l'API.
La fixture request
Playwright expose un APIRequestContext via la fixture request. Elle est disponible dans n'importe quel test de la même façon que page :
import { test, expect } from '@playwright/test';
test('GET /api/items renvoie une liste', async ({ request }) => {
const response = await request.get('https://lab.becomeqa.com/api/items');
expect(response.status()).toBe(200);
const items = await response.json();
expect(items.length).toBeGreaterThan(0);
});Aucun navigateur ne s'ouvre. Aucune page ne se charge. Juste une requête HTTP et une réponse. La fixture request gère les cookies, les headers et la configuration de l'URL de base automatiquement.
Requêtes GET et validation de la réponse
Le pattern de test API le plus courant : appeler un endpoint, vérifier le statut, vérifier la forme des données.
test('GET /api/items renvoie la bonne structure', async ({ request }) => {
const response = await request.get('https://lab.becomeqa.com/api/items');
// Vérifier le statut HTTP
expect(response.status()).toBe(200);
expect(response.ok()).toBeTruthy(); // true pour les codes 2xx
// Parser le corps de la réponse
const items = await response.json();
// Vérifier le tableau
expect(Array.isArray(items)).toBe(true);
expect(items.length).toBeGreaterThan(0);
// Vérifier la forme du premier élément
const firstItem = items[0];
expect(firstItem).toHaveProperty('id');
expect(firstItem).toHaveProperty('destination');
expect(firstItem).toHaveProperty('status');
});response.ok() est un raccourci pour status >= 200 && status < 300. Utilisez-le quand vous voulez seulement vérifier que la requête a réussi.
Pour vérifier les headers de réponse :
const contentType = response.headers()['content-type'];
expect(contentType).toContain('application/json');Requêtes POST
Les requêtes POST envoient des données pour créer une ressource. Passez le corps en JSON :
test('POST /api/items crée un nouvel élément', async ({ request }) => {
const response = await request.post('https://lab.becomeqa.com/api/items', {
data: {
destination: 'Tokyo',
status: 'planned',
notes: 'Saison des cerisiers'
}
});
expect(response.status()).toBe(201);
const created = await response.json();
expect(created).toHaveProperty('id');
expect(created.destination).toBe('Tokyo');
expect(created.status).toBe('planned');
});L'option data sérialise automatiquement en JSON et définit le header Content-Type: application/json.
Pour les requêtes encodées en form, utilisez form à la place de data :
const response = await request.post('/api/login', {
form: {
username: 'admin@becomeqa.com',
password: 'testpass123'
}
});Authentification
La plupart des APIs réelles exigent une authentification. Deux patterns courants.
Token Bearer dans le header :test('GET authentifié renvoie les données utilisateur', async ({ request }) => {
// D'abord, obtenir un token
const loginResponse = await request.post('https://lab.becomeqa.com/api/auth/login', {
data: {
username: 'admin@becomeqa.com',
password: 'testpass123'
}
});
const { token } = await loginResponse.json();
// Utiliser le token dans les requêtes suivantes
const response = await request.get('https://lab.becomeqa.com/api/items', {
headers: {
'Authorization': `Bearer ${token}`
}
});
expect(response.status()).toBe(200);
});Si votre application utilise des cookies pour l'authentification, vous pouvez vous connecter via le navigateur et réutiliser la session pour les appels API. L'objet page.request partage les cookies avec le contexte du navigateur :
test('appel API avec la session du navigateur', async ({ page, request }) => {
// Se connecter via l'UI
await page.goto('https://lab.becomeqa.com');
await page.getByRole('button', { name: 'Login' }).click();
await page.getByLabel('Username').fill('admin@becomeqa.com');
await page.getByLabel('Password').fill('testpass123');
await page.getByRole('button', { name: 'Submit' }).click();
// Utiliser page.request — il porte le cookie d'authentification
const response = await page.request.get('https://lab.becomeqa.com/api/items');
expect(response.status()).toBe(200);
});Notez la différence : request (la fixture) est un contexte autonome sans cookies du navigateur. page.request partage les cookies avec la session navigateur courante.
Tester les réponses d'erreur
Les APIs doivent renvoyer des erreurs significatives. Testez-les explicitement.
test('GET /api/items/:id renvoie 404 pour un ID inconnu', async ({ request }) => {
const response = await request.get('https://lab.becomeqa.com/api/items/nonexistent-id-99999');
expect(response.status()).toBe(404);
const body = await response.json();
expect(body).toHaveProperty('error');
});
test('POST /api/items renvoie 400 si les champs obligatoires manquent', async ({ request }) => {
const response = await request.post('https://lab.becomeqa.com/api/items', {
data: {
// champ 'destination' obligatoire manquant
status: 'planned'
}
});
expect(response.status()).toBe(400);
});
test('l\'endpoint protégé renvoie 401 sans auth', async ({ request }) => {
const response = await request.get('https://lab.becomeqa.com/api/items');
// pas de header Authorization
expect(response.status()).toBe(401);
});Ces tests vérifient que l'API gère correctement les entrées invalides, pas seulement le chemin nominal.
Combiner tests API et tests UI
L'un des patterns les plus puissants : utiliser l'API pour préparer les données de test, puis les vérifier via l'UI. L'inverse fonctionne aussi, en interagissant via l'UI et en vérifiant via l'API.
Préparation API, vérification UI :test('l\'élément créé apparaît dans le tableau UI', async ({ page, request }) => {
// Créer l'élément via API : rapide et fiable
const createResponse = await request.post('https://lab.becomeqa.com/api/items', {
data: { destination: 'Lisbonne', status: 'planned' }
});
const { id } = await createResponse.json();
// Vérifier qu'il apparaît dans l'UI
await page.goto('https://lab.becomeqa.com');
// ... étapes de connexion ...
await expect(page.getByText('Lisbonne')).toBeVisible();
// Nettoyage via API après le test
await request.delete(`https://lab.becomeqa.com/api/items/${id}`);
});Ce test est plus rapide et plus fiable. La préparation via UI est la source de fragilité la plus courante dans les tests qui ne testent pas réellement l'UI.
Action UI, vérification API :test('supprimer un élément via UI le retire de l\'API', async ({ page, request }) => {
// Préparation via API
const createResponse = await request.post('https://lab.becomeqa.com/api/items', {
data: { destination: 'Oslo', status: 'planned' }
});
const { id } = await createResponse.json();
// Action via UI
await page.goto('https://lab.becomeqa.com');
// ... connexion, trouver l'élément, cliquer sur supprimer ...
// Vérification via API
const checkResponse = await request.get(`https://lab.becomeqa.com/api/items/${id}`);
expect(checkResponse.status()).toBe(404);
});Configurer une URL de base pour les tests API
Répéter l'URL complète dans chaque test est fastidieux. Définissez baseURL dans playwright.config.ts :
export default defineConfig({
use: {
baseURL: 'https://lab.becomeqa.com',
},
});Vous pouvez ensuite utiliser des chemins relatifs :
const response = await request.get('/api/items');
const response = await request.post('/api/items', { data: { ... } });Pour les projets avec des URLs de base distinctes pour l'UI et l'API, créez une fixture personnalisée qui configure le contexte API séparément.
extraHTTPHeaders à la configuration si votre API exige un header commun sur chaque requête (une clé API ou un header X-App-Version personnalisé). Définissez-le une seule fois dans la config plutôt que dans chaque test.export default defineConfig({
use: {
baseURL: 'https://lab.becomeqa.com',
extraHTTPHeaders: {
'X-Api-Key': process.env.API_KEY || '',
},
},
});Organiser les tests API séparément
Gardez les tests API dans leur propre dossier pour pouvoir les exécuter indépendamment des tests UI :
tests/
ui/
login.spec.ts
items.spec.ts
api/
items-api.spec.ts
auth-api.spec.ts# Lancer uniquement les tests API : rapide, pas de navigateur
npx playwright test tests/api/
# Lancer uniquement les tests UI
npx playwright test tests/ui/Les tests API s'exécutent nettement plus vite que les tests UI. Les lancer séparément en CI vous donne un feedback rapide sur les bugs backend avant que la suite UI plus lente se termine.
FAQ
Ai-je encore besoin de Postman si j'utilise Playwright pour les tests API ?Postman est utile pour l'exploration manuelle des APIs : comprendre quels endpoints existent, quels paramètres ils acceptent, à quoi ressemblent les réponses. Une fois que vous savez ce que vous testez, écrivez la version automatisée dans Playwright. Les deux ont leur place.
Chaque endpoint API doit-il avoir un test ?Concentrez-vous sur les endpoints qui comptent : l'authentification, les opérations de données principales, et tout endpoint avec une logique de validation complexe. Une API CRUD pour les éléments de voyage nécessite des tests pour créer, lire, mettre à jour, supprimer, et au moins les cas d'erreur principaux (404, 400, 401). Pas chaque combinaison d'entrées.
Comment tester les uploads de fichiers via API ?Utilisez l'option multipart :
const response = await request.post('/api/upload', {
multipart: {
file: {
name: 'test.pdf',
mimeType: 'application/pdf',
buffer: Buffer.from('contenu pdf de test'),
}
}
});Oui. Les tests qui n'utilisent que request et pas page s'exécutent sans ouvrir de navigateur du tout. Ce sont des tests HTTP purs et ils s'exécutent aussi vite que n'importe quel autre client HTTP.