Chaque test Playwright est un client qui envoie des requêtes HTTP à un serveur. Quand les tests échouent avec "net::ERR_CONNECTION_REFUSED", "SSL_ERROR_RX_RECORD_TOO_LONG" ou "504 Gateway Timeout", ces erreurs ont une signification précise. Comprendre ce qui se passe entre votre test et le serveur transforme un message d'erreur opaque en cinq minutes de débogage.
Ce qui se passe quand Playwright ouvre une page
Quand votre test exécute await page.goto('https://lab.becomeqa.com'), quatre choses se produisent dans l'ordre.
1. Résolution DNS
Votre ordinateur interroge un serveur DNS : "Quelle adresse IP correspond à lab.becomeqa.com ?"
Le serveur DNS répond avec quelque chose comme 104.21.8.42. Les noms de domaine sont des alias lisibles. Le réseau réel utilise des adresses IP.
ERR_NAME_NOT_RESOLVED: le DNS n'a pas trouvé le domaine. Soit le domaine n'existe pas, soit votre serveur DNS est en panne, soit (en CI) le conteneur ne peut pas atteindre le DNS externe.- Dans les environnements CI, les noms d'hôte internes comme
api.internalnécessitent une configuration réseau correcte pour que le DNS fonctionne.
2. Connexion TCP
Votre ordinateur ouvre une connexion TCP vers cette adresse IP sur un numéro de port.
- Port 443 = HTTPS (chiffré)
- Port 80 = HTTP (non chiffré)
- Port 3000, 8080, etc. = serveurs de développement
ERR_CONNECTION_REFUSED: rien n'écoute sur ce port. Le serveur ne tourne pas, ou vous tapez sur le mauvais port.ERR_CONNECTION_TIMED_OUT: le serveur est inaccessible (pare-feu, mauvais réseau, serveur en panne).- En CI, ça signifie souvent que votre serveur d'application n'a pas fini de démarrer avant que le test ne s'exécute.
3. Handshake TLS (HTTPS uniquement)
Le navigateur et le serveur échangent des certificats et négocient le chiffrement. C'est le "S" dans HTTPS.
Ce qui casse ici :ERR_CERT_AUTHORITY_INVALID: le certificat SSL est auto-signé ou expiré. Fréquent dans les environnements de staging.ERR_SSL_PROTOCOL_ERROR: incompatibilité de version TLS ou mauvaise configuration.- Playwright dispose de
ignoreHTTPSErrors: truedans la config pour ignorer la validation des certificats dans les environnements de test :
// playwright.config.ts
use: {
ignoreHTTPSErrors: true, // pour le staging avec des certs auto-signés
}4. Requête et réponse HTTP
Le transfert de contenu a lieu. Votre navigateur envoie une requête ; le serveur renvoie une réponse.
Une requête ressemble à ceci (simplifiée) :
GET /dashboard HTTP/1.1
Host: lab.becomeqa.com
Cookie: session=abc123
Accept: text/htmlUne réponse ressemble à ceci :
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 4521
<!DOCTYPE html>...Le code de statut indique ce qui s'est passé.
Codes de statut HTTP que vous rencontrerez
| Code | Signification | Cause fréquente |
|------|--------------|-----------------|
| 200 | OK | Tout a fonctionné |
| 201 | Created | POST réussi, ressource créée |
| 204 | No Content | Succès sans body (courant pour DELETE) |
| 301/302 | Redirect | Page déplacée, le navigateur suit automatiquement |
| 400 | Bad Request | Requête malformée : mauvais format, champ manquant |
| 401 | Unauthorized | Non authentifié : besoin de se connecter |
| 403 | Forbidden | Authentifié mais non autorisé : mauvais rôle ou permissions |
| 404 | Not Found | La ressource n'existe pas à ce chemin |
| 422 | Unprocessable Entity | Format valide mais contenu qui échoue à la validation |
| 429 | Too Many Requests | Rate limiting déclenché |
| 500 | Internal Server Error | Bug dans le code serveur |
| 502 | Bad Gateway | Le serveur a reçu une mauvaise réponse d'un service en amont |
| 503 | Service Unavailable | Serveur en panne ou surchargé |
| 504 | Gateway Timeout | Le service en amont a mis trop de temps à répondre |
Un 401 est un bug différent d'un 403. "Non connecté" vs "connecté mais non autorisé" se ressemblent dans l'UI (les deux redirigent vers la connexion ou affichent une erreur), mais ils nécessitent des corrections différentes.
Un 422 signifie que votre requête a atteint le serveur et que le serveur l'a comprise, mais que les données ont échoué à la validation. Un 400 signifie que le serveur n'a pas pu analyser la requête.
Méthodes HTTP : ce que chacune signifie
Chaque requête utilise une méthode qui décrit l'opération attendue :
| Méthode | Usage | Idempotente ? |
|---------|-------|--------------|
| GET | Lire des données, charger une page, récupérer une liste | Oui. Appeler deux fois donne le même résultat |
| POST | Créer, soumettre un formulaire, ajouter un enregistrement | Non. Appeler deux fois crée deux enregistrements |
| PUT | Remplacer, mettre à jour une ressource entière | Oui. Appeler deux fois donne le même résultat |
| PATCH | Mise à jour partielle, modifier un seul champ | En général oui |
| DELETE | Supprimer une ressource | Oui. Supprimer deux fois donne le même résultat |
Si votre test appelle POST deux fois (par exemple, un clic sur un bouton est enregistré deux fois), vous devriez obtenir deux enregistrements en base de données. Si vous testez l'idempotence d'un endpoint PUT, l'appeler deux fois doit laisser le système dans le même état qu'un seul appel.
Quand vous voyez un bug du type "le formulaire a été soumis deux fois", vous avez besoin de savoir s'il y a eu deux requêtes POST ou une seule. C'est visible dans l'onglet Network de DevTools.
Headers : métadonnées sur chaque requête et réponse
Les headers transportent des métadonnées. Ce n'est pas le contenu principal, mais ils contrôlent la façon dont la requête et la réponse sont traitées.
Headers de requête que vous verrez dans les tests :Authorization: Bearer eyJhbGciOiJIUzI1NiJ9... ← token d'authentification
Content-Type: application/json ← "j'envoie du JSON"
Cookie: session=abc123; csrf_token=xyz ← cookies de session
Accept: application/json ← "je veux du JSON en retour"Content-Type: application/json; charset=utf-8 ← la réponse est du JSON
Set-Cookie: session=abc123; HttpOnly; Secure ← le serveur pose un cookie
Cache-Control: no-store ← ne pas mettre en cache cette réponse
Location: /dashboard ← cible de la redirection (avec 302)test('API returns JSON content type', async ({ request }) => {
const response = await request.get('https://lab.becomeqa.com/api/items');
expect(response.status()).toBe(200);
expect(response.headers()['content-type']).toContain('application/json');
});Cookies et sessions : comment fonctionne "être connecté"
Quand vous vous connectez, le serveur crée une session et renvoie un cookie :
Set-Cookie: session_id=abc123; HttpOnly; Secure; SameSite=StrictVotre navigateur stocke ce cookie et l'envoie avec chaque requête suivante :
Cookie: session_id=abc123Le serveur lit le cookie, retrouve la session, et sait qui vous êtes.
C'est pourquoi storageState de Playwright fonctionne. Il sauvegarde les cookies d'une session connectée dans un fichier, puis les charge dans de nouveaux contextes de navigateur. Le serveur voit le même cookie, pense que vous êtes toujours connecté, et ignore l'authentification.
// Sauvegarder l'état d'authentification après connexion
await page.context().storageState({ path: 'auth.json' });
// Le charger dans les tests
use: {
storageState: 'auth.json'
}Quand un test échoue avec "redirigé vers la page de connexion", le cookie de session a expiré ou n'a pas été sauvegardé correctement. Vérifiez aussi que le fichier auth.json n'est pas obsolète.
Lire le trafic réseau avec Playwright
Playwright peut intercepter et inspecter chaque requête que votre test effectue :
test('login sends correct credentials', async ({ page }) => {
// Écouter l'appel API de connexion
const loginRequest = page.waitForRequest('**/api/auth/login');
await page.goto('/');
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();
const request = await loginRequest;
const body = request.postDataJSON();
expect(body.email).toBe('admin@becomeqa.com');
});Et les réponses :
test('items API returns correct data structure', async ({ page }) => {
// Intercepter l'appel API effectué au chargement de la page
const itemsResponse = page.waitForResponse('**/api/items');
await page.goto('/dashboard');
const response = await itemsResponse;
expect(response.status()).toBe(200);
const items = await response.json();
expect(Array.isArray(items)).toBeTruthy();
});Erreurs courantes et ce qu'elles signifient
"net::ERR_CONNECTION_REFUSED" en CI mais pas en localVotre serveur d'application ne tourne pas quand le test démarre. Ajoutez une attente que le serveur soit prêt, ou utilisez la config webServer dans playwright.config.ts :
webServer: {
command: 'npm start',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
}Session expirée ou état d'authentification non chargé. Vérifiez que storageState est configuré, ou que votre setup global s'exécute avant les tests.
Votre test touche un rate limiter. Ajoutez des délais entre les requêtes ou désactivez le rate limiting dans les environnements de test.
"Response timeout: 30000ms exceeded"Le serveur a mis plus de 30 secondes à répondre. Soit augmentez le timeout pour cette opération spécifique, soit cherchez pourquoi le serveur est lent (requête base de données, appel API externe, etc.).
Le test passe en local, échoue en CI avec une erreur 500Variable d'environnement manquante en CI : URL de base de données, clé API, feature flag. Le serveur lève une erreur non gérée parce qu'une valeur de configuration requise est undefined.
L'onglet Network de DevTools
Pour déboguer un échec, ouvrez Chrome DevTools, onglet Network, avant de rejouer le flux manuellement. Chaque requête apparaît avec :
- L'URL et la méthode
- Le code de statut
- Les headers et le body de la requête
- Les headers et le body de la réponse
- Le timing
Si l'UI affiche une erreur mais que l'onglet Network montre un 200, le bug est dans le JavaScript frontend qui interprète la réponse. Si l'onglet Network montre un 500, le bug est dans le backend.
Le trace viewer de Playwright affiche les mêmes informations pour les runs de tests automatisés : ouvrez la trace, basculez sur l'onglet Network, et voyez chaque requête effectuée pendant le test.
Ce que vous n'avez pas besoin de savoir pour l'instant
- WebSockets et Server-Sent Events : connexions en temps réel, pertinentes uniquement pour tester des fonctionnalités temps réel
- HTTP/2 et HTTP/3 : versions du protocole ; Playwright les gère de façon transparente
- Gestion des certificats TLS : domaine DevOps, pas du QA au quotidien
- Load balancing et configuration CDN : connaissance infrastructure pour les rôles senior
- Détails du flux OAuth 2.0 : utile spécifiquement pour tester des fonctionnalités d'authentification
FAQ
Dois-je connaître le réseau pour écrire des tests Playwright ?Pas en profondeur. Comprendre le flux en quatre étapes (DNS, TCP, TLS, HTTP) permet de diagnostiquer la classe d'erreur en 30 secondes au lieu de 30 minutes. Vous n'avez pas besoin de l'implémenter. Vous avez juste besoin de reconnaître quelle couche a lâché.
Dois-je regarder l'onglet Network ou le message d'erreur du test quand un test échoue ?Les deux, dans l'ordre. Le message d'erreur du test indique quelle assertion a échoué. L'onglet Network (ou la trace Playwright) indique pourquoi : ce que le serveur a réellement renvoyé. Message d'erreur d'abord, réseau ensuite.
Quelle est la différence entre un 401 et un 403 ?401 = non authentifié (vous n'êtes pas connecté). 403 = authentifié mais non autorisé (vous êtes connecté, mais vous n'avez pas la permission pour cette ressource). Les deux peuvent produire un message "accès refusé" dans l'UI, mais ce sont des bugs différents avec des corrections différentes.
→ See also: Tests d'API avec Playwright: Au-delà de l'Interface | SQL pour QA: Les Requêtes dont Vous Avez Vraiment Besoin