Le bloc try/catch de JavaScript gère à la fois les exceptions synchrones et les promises rejetées quand il est combiné avec async/await. Comprendre ce mécanisme évite les échecs silencieux et les messages d'erreur trompeurs dans le code Playwright.
Les bases : try/catch
Un bloc try/catch permet d'exécuter du code et de gérer les erreurs sans planter le programme :
try {
// Code qui peut lancer une erreur
const result = JSON.parse('not valid json');
} catch (error) {
// S'exécute si le bloc try lance une erreur
console.log('Parsing failed:', error.message);
}Sans try/catch, JSON.parse('not valid json') ferait planter votre script avec une erreur non gérée. Avec try/catch, vous la traitez proprement.
La variable error dans le bloc catch est un objet Error avec trois propriétés : error.message (description lisible), error.name (type d'erreur, comme 'SyntaxError'), et error.stack (stack trace complète).
Le bloc finally
finally s'exécute qu'une erreur se soit produite ou non. Utile pour le nettoyage :
test('file upload with cleanup', async ({ page }) => {
const tempFile = await createTempFile();
try {
await page.setInputFiles('[data-testid="upload"]', tempFile);
await expect(page.locator('[data-testid="upload-success"]')).toBeVisible();
} catch (error) {
console.error('Upload test failed:', error.message);
throw error; // Re-throw pour que le test échoue quand même
} finally {
await deleteTempFile(tempFile); // Nettoyage garanti, même en cas d'échec
}
});finally s'exécute toujours. C'est particulièrement précieux pour le code de nettoyage dans les tests.
async/await et les erreurs
Dans Playwright, presque tout est async. Les erreurs dans le code asynchrone fonctionnent de la même façon : il suffit de placer await au bon endroit.
// Sans try/catch : rejet non géré = test qui échoue avec une erreur peu claire
async function getUserFromApi() {
const response = await fetch('/api/user/999');
const data = await response.json();
return data;
}
// Avec try/catch : rejet géré = vous contrôlez le message d'erreur
async function getUserFromApi() {
try {
const response = await fetch('/api/user/999');
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Failed to fetch user:', error.message);
throw error; // Re-throw si vous voulez que l'appelant gère aussi l'erreur
}
}Lancer ses propres erreurs
On peut lancer des erreurs intentionnellement pour signaler un échec :
function validateEmail(email: string): void {
if (!email.includes('@')) {
throw new Error(`Invalid email format: "${email}"`);
}
if (email.length > 254) {
throw new Error(`Email too long: ${email.length} characters (max 254)`);
}
}
try {
validateEmail('not-an-email');
} catch (error) {
console.log(error.message); // 'Invalid email format: "not-an-email"'
}Dans les tests, lancer une erreur permet d'échouer avec un message précis plutôt qu'un échec d'assertion générique :
async function getAuthToken(request: APIRequestContext): Promise<string> {
const response = await request.post('/api/auth/login', {
data: { email: 'test@test.com', password: 'TestPass1' },
});
if (response.status() !== 200) {
throw new Error(`Login failed: ${response.status()}, cannot proceed with tests`);
}
const { token } = await response.json();
if (!token) {
throw new Error('Login response missing token field');
}
return token;
}Types d'erreurs
JavaScript dispose de plusieurs types d'erreurs natifs :
| Type d'erreur | Quand il apparaît |
|--------------|-------------------|
| Error | Erreur générique (classe de base) |
| TypeError | Mauvais type : null.something, appel d'une non-fonction |
| SyntaxError | JavaScript invalide ou erreur de parsing JSON |
| RangeError | Valeur hors plage : new Array(-1) |
| ReferenceError | Variable inexistante |
On peut vérifier le type pour traiter différentes erreurs différemment :
try {
await doSomething();
} catch (error) {
if (error instanceof TypeError) {
console.log('Type error — check your data types');
} else if (error instanceof SyntaxError) {
console.log('Syntax error — invalid JSON or similar');
} else {
throw error; // Erreur inconnue : re-throw
}
}Patterns courants dans les tests Playwright
Relancer une opération en cas d'échec
async function waitForApiReady(request: APIRequestContext, maxAttempts = 5): Promise<void> {
for (let i = 0; i < maxAttempts; i++) {
try {
const response = await request.get('/api/health');
if (response.status() === 200) return;
} catch (error) {
// L'API n'est pas encore prête, on réessaie
}
await new Promise(resolve => setTimeout(resolve, 1000));
}
throw new Error(`API not ready after ${maxAttempts} attempts`);
}Collecter les erreurs sans s'arrêter
const errors: string[] = [];
for (const email of testEmails) {
try {
await validateEmailField(page, email);
} catch (error) {
errors.push(`${email}: ${error.message}`);
}
}
if (errors.length > 0) {
throw new Error(`Validation failures:\n${errors.join('\n')}`);
}Classe d'erreur personnalisée pour un code plus lisible
class TestDataError extends Error {
constructor(message: string) {
super(message);
this.name = 'TestDataError';
}
}
// On distingue maintenant les erreurs de setup des erreurs d'assertion
try {
const user = await createTestUser(request);
} catch (error) {
if (error instanceof TestDataError) {
console.error('Test setup failed — skipping test');
test.skip();
}
throw error;
}Les erreurs Playwright que vous verrez
TimeoutError
L'erreur Playwright la plus fréquente. Un locator n'est pas devenu visible dans le délai imparti :
TimeoutError: locator.click: Timeout 30000ms exceeded.
Locator: [data-testid="submit-button"]Dans la plupart des cas, Playwright la gère et fait échouer le test avec un message clair. Mais si vous voulez traiter un timeout proprement (tenter autre chose si un élément n'apparaît pas), c'est possible :
try {
await page.locator('[data-testid="popup"]').click({ timeout: 2000 });
} catch (error) {
// Le popup n'est pas apparu en 2 secondes : c'est acceptable
// On continue le test sans interagir avec le popup
}Rejet de promise non géré
Si vous appelez une fonction async sans await et qu'elle lance une erreur, celle-ci peut être un "unhandled rejection". Elle ne fait pas échouer le test immédiatement et le message d'erreur peut prêter à confusion.
// Problématique : pas d'await, l'erreur peut disparaître silencieusement
page.goto('/admin'); // En cas d'échec, le test peut passer dans un mauvais état
// Correct : l'erreur remonte immédiatement
await page.goto('/admin');Toujours await les opérations async dans les tests.
Quand ne pas utiliser try/catch dans les tests
Ne jamais utiliser try/catch pour masquer des échecs d'assertion. Si une assertion échoue, le test doit échouer :
// Incorrect : l'échec d'assertion est avalé, le test "passe" alors qu'il ne devrait pas
try {
await expect(page.locator('[data-testid="success"]')).toBeVisible();
} catch (error) {
console.log('Element not visible but continuing...');
}
// Correct : laisser les échecs d'assertion faire échouer le test
await expect(page.locator('[data-testid="success"]')).toBeVisible();try/catch dans les tests sert trois usages : le code de setup/teardown, la résilience intentionnelle (retries, interactions optionnelles), et la création de données de test avec des messages d'erreur sur mesure.
Ce n'est pas un outil pour silencer les assertions.
Récapitulatif
try/catchgère les erreurs : le code danstrys'exécute,catchtraite l'erreur lancéefinallys'exécute toujours : à utiliser pour le nettoyage- Toujours
awaitles opérations async : les échecs non attendus sont difficiles à déboguer throwintentionnellement quand vous voulez un message d'erreur précis- Ne jamais capturer les échecs d'assertion : ils doivent faire échouer le test
instanceofpermet de vérifier le type d'erreur et de traiter différemment chaque cas
La gestion des erreurs sert à rendre le code plus résilient et les messages d'erreur plus informatifs. Pas à cacher les échecs.
→ See also: JavaScript pour les QA Engineers: Le Minimum pour Commencer à Automatiser | Async/Await en Termes Simples (pour les Testeurs Perdus avec les Promesses) | Comment Lire les Messages d'Erreur de Playwright