Maîtriser l'Automatisation de Navigateurs avec Playwright et Puppeteer
Maîtriser l'Automatisation de Navigateurs avec Playwright et Puppeteer

Débogage, Gestion des Erreurs et Bonnes Pratiques pour des Scripts d'Automatisation Robustes

Introduction

Bienvenue à cette leçon essentielle sur le débogage, la gestion des erreurs et les bonnes pratiques pour construire des scripts d'automatisation de navigateurs robustes. Dans le monde de l'automatisation, en particulier avec des outils puissants comme Playwright et Puppeteer, nous interagissons avec des systèmes externes (navigateurs, serveurs web, réseaux) qui sont intrinsèquement imprévisibles. Un script d'automatisation, même le plus simple, peut échouer pour une multitude de raisons : un élément qui ne charge pas à temps, une modification subtile de l'interface utilisateur, une erreur réseau temporaire, ou même une logique d'application côté client inattendue.

Sans une approche méthodique du débogage et une gestion proactive des erreurs, vos scripts seront fragiles, difficiles à maintenir et peu fiables. Cette leçon vous fournira les outils et les stratégies nécessaires pour transformer vos scripts d'automatisation en solutions résilientes, capables de naviguer dans le chaos du web réel avec grâce et fiabilité.

Nous aborderons trois piliers fondamentaux :

  1. Le débogage efficace : Comment trouver et comprendre la source de vos problèmes.
  2. La gestion des erreurs : Comment anticiper et réagir aux imprévus.
  3. Les bonnes pratiques : Comment écrire du code d'automatisation qui dure.

Préparez-vous à renforcer la fondation de tous vos futurs projets d'automatisation !


1. Le Débogage Efficace de Scripts d'Automatisation

Le débogage est l'art de trouver et de corriger les bogues. Pour les scripts d'automatisation de navigateurs, cela implique de comprendre non seulement votre code, mais aussi comment il interagit avec le navigateur et la page web à un moment donné.

1.1. Les Méthodes de Débogage Standard

A. console.log() : L'ami de toujours

C'est la méthode la plus basique mais souvent la plus efficace. Disperser des console.log() stratégiques dans votre code peut vous aider à :

  • Suivre le flux d'exécution de votre script.
  • Inspecter la valeur des variables à des points clés.
  • Confirmer si certaines parties du code sont atteintes ou non.
// Exemple avec Playwright
const { chromium } = require('playwright');

(async () => {
  console.log('Début du script...');
  const browser = await chromium.launch();
  const page = await browser.newPage();
  console.log('Navigateur lancé et nouvelle page créée.');

  try {
    const url = 'https://www.example.com';
    await page.goto(url);
    console.log(`Navigué vers : ${url}`);

    const title = await page.title();
    console.log(`Titre de la page : ${title}`);

    // Tentative de cliquer sur un bouton
    const buttonSelector = '#nonExistentButton';
    console.log(`Tentative de cliquer sur : ${buttonSelector}`);
    await page.click(buttonSelector, { timeout: 3000 }); // Ajout d'un timeout pour l'exemple
    console.log('Bouton cliqué avec succès.');

  } catch (error) {
    console.error(`Une erreur est survenue : ${error.message}`);
  } finally {
    await browser.close();
    console.log('Navigateur fermé. Fin du script.');
  }
})();

Dans cet exemple, les console.log nous donneront une trace de l'exécution et des valeurs intermédiaires, ce qui est très utile pour identifier où le script s'arrête ou dévie.

B. Points d'arrêt et Débogueurs (Node.js Inspector, VS Code)

Pour un débogage plus interactif et puissant, utilisez le débogueur intégré de Node.js, souvent couplé à votre IDE (comme VS Code).

  1. Ajoutez debugger; dans votre code : C'est l'équivalent programmatique d'un point d'arrêt.
  2. Exécutez votre script avec l'inspecteur Node.js :
    node --inspect-brk votreScript.js
    
    L'--inspect-brk mettra en pause l'exécution dès la première ligne, vous permettant de joindre un débogueur. --inspect permet de joindre un débogueur à tout moment.
  3. Joignez le débogueur :
    • VS Code : Ouvrez le script, allez dans l'onglet "Exécuter et déboguer", et lancez une configuration "Node.js: lancer le programme". VS Code détectera automatiquement la session d'inspection. Vous pouvez aussi simplement définir des points d'arrêt directement dans l'éditeur.
    • Google Chrome DevTools : Ouvrez Chrome, tapez chrome://inspect dans la barre d'adresse, et cliquez sur "Open dedicated DevTools for Node".

Cela vous permettra d'exécuter votre code pas à pas, d'inspecter l'état des variables, de modifier leur valeur et d'évaluer des expressions arbitraires.

// Exemple avec Playwright et 'debugger;'
const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch();
  const page = await browser.newPage();

  await page.goto('https://www.example.com');

  // Ici, le débogueur se mettra en pause si le script est exécuté avec --inspect-brk
  debugger;

  const title = await page.title();
  console.log(`Titre : ${title}`);

  const headingText = await page.locator('h1').textContent();
  console.log(`Titre de la section : ${headingText}`);

  await browser.close();
})();

1.2. Outils de Débogage Spécifiques à Playwright et Puppeteer

Ces frameworks offrent des fonctionnalités uniques pour le débogage visuel et interactif, qui sont inestimables.

A. Playwright Inspector (PWDEBUG=1)

C'est l'outil de débogage le plus puissant de Playwright. Il lance le navigateur en mode non headless, ouvre les DevTools et affiche une interface utilisateur de débogage.

Pour l'activer, exécutez votre script avec la variable d'environnement PWDEBUG définie sur 1 :

PWDEBUG=1 node votreScript.js

Avec PWDEBUG=1, vous pouvez :

  • Exécuter pas à pas votre code Playwright.
  • Mettre en surbrillance les éléments sur lesquels Playwright va interagir.
  • Explorer le DOM du navigateur comme dans les DevTools classiques.
  • Enregistrer de nouvelles actions pour générer du code Playwright.
  • Voir l'historique des actions Playwright.

B. page.pause() (Playwright)

Cette méthode est similaire à debugger; mais pour le contexte du navigateur. Lorsque page.pause() est appelée, Playwright met en pause l'exécution et ouvre le Playwright Inspector (si PWDEBUG=1 n'est pas déjà actif, il le forcera).

// Exemple avec page.pause()
const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch({ headless: false }); // Mode non-headless pour voir l'action
  const page = await browser.newPage();

  await page.goto('https://www.google.com');

  console.log('La page Google est chargée. Le script va maintenant se mettre en pause.');
  await page.pause(); // Le script se met en pause ici.

  // Vous pouvez interagir avec la page via l'inspecteur Playwright
  // ou reprendre l'exécution manuellement.

  await page.fill('textarea[name="q"]', 'Playwright automation');
  await page.press('textarea[name="q"]', 'Enter');

  console.log('Recherche effectuée. Le script va se mettre en pause une seconde fois.');
  await page.pause();

  await browser.close();
})();

Exécuter ce script avec node votreScript.js ouvrira le navigateur, naviguera vers Google, puis mettra en pause l'exécution, ouvrant l'inspecteur Playwright.

C. Options de Lancement du Navigateur

  • headless: false : Lance le navigateur avec une interface utilisateur visible. C'est indispensable pour comprendre ce qui se passe visuellement.
    const browser = await chromium.launch({ headless: false });
    
  • slowMo : Ralentit l'exécution de chaque action Playwright (en millisecondes), ce qui vous permet de mieux observer les interactions.
    const browser = await chromium.launch({ headless: false, slowMo: 500 }); // 500ms de délai
    

D. Captures d'Écran et Enregistrements Vidéo en Cas d'Échec

Souvent, la meilleure façon de déboguer un échec intermittent est de voir ce qui s'est passé au moment de l'erreur. Playwright et Puppeteer permettent de prendre des captures d'écran ou d'enregistrer des vidéos.

// Exemple de capture d'écran en cas d'erreur (Playwright)
const { chromium } = require('playwright');
const fs = require('fs');
const path = require('path');

(async () => {
  const browser = await chromium.launch();
  const page = await browser.newPage();

  try {
    await page.goto('https://www.example.com');
    await page.click('#nonExistentElement'); // Provoquer une erreur
  } catch (error) {
    console.error(`Erreur détectée : ${error.message}`);
    const screenshotPath = path.join(__dirname, 'error-screenshot.png');
    await page.screenshot({ path: screenshotPath });
    console.log(`Capture d'écran de l'erreur enregistrée : ${screenshotPath}`);
  } finally {
    await browser.close();
  }
})();

Playwright Test fournit des options intégrées pour cela (voir playwright.config.ts pour screenshot: 'only-on-failure', video: 'retain-on-failure').


2. La Gestion des Erreurs pour des Scripts Tolérants aux Pannes

Même le meilleur débogage ne peut pas empêcher toutes les erreurs. Une gestion robuste des erreurs est cruciale pour que vos scripts puissent :

  • Récupérer des situations inattendues.
  • Fournir des messages d'erreur clairs.
  • Éviter de planter complètement.
  • Garantir qu'un certain état est atteint ou nettoyé.

2.1. Les Blocs try...catch

C'est la pierre angulaire de la gestion des erreurs en JavaScript. Un bloc try exécute du code, et si une erreur est lancée à l'intérieur, l'exécution est transférée au bloc catch. Le bloc finally (optionnel) est toujours exécuté, qu'une erreur se soit produite ou non, ce qui est idéal pour les opérations de nettoyage.

// Exemple basique de try...catch
const performAction = async () => {
  try {
    console.log('Tentative d\'une action...');
    // Simule une action qui pourrait échouer
    const result = await someRiskyOperation();
    console.log('Action réussie:', result);
    return result;
  } catch (error) {
    console.error('L\'action a échoué:', error.message);
    // On peut relancer l'erreur, logguer, ou retourner une valeur par défaut
    throw new Error('Échec de l\'opération critique.');
  } finally {
    console.log('Le bloc finally est toujours exécuté (nettoyage si nécessaire).');
  }
};

const someRiskyOperation = async () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const shouldFail = Math.random() > 0.5; // 50% de chance d'échec
      if (shouldFail) {
        reject(new Error('Erreur simulée lors de l\'opération.'));
      } else {
        resolve('Opération réussie !');
      }
    }, 1000);
  });
};

(async () => {
  console.log('\n--- Tentative 1 ---');
  await performAction().catch(err => console.error('Erreur finale catch:', err.message));

  console.log('\n--- Tentative 2 ---');
  await performAction().catch(err => console.error('Erreur finale catch:', err.message));
})();

2.2. Gestion des Erreurs Spécifiques aux Automatisations

A. Timeouts

Les timeouts sont la cause la plus fréquente d'échec dans les scripts d'automatisation. Les éléments peuvent mettre du temps à apparaître, les réseaux peuvent être lents. Playwright et Puppeteer lancent des TimeoutError lorsque les opérations dépassent la limite de temps configurée.

  • Global Timeout : Peut être configuré dans playwright.config.ts (test.timeout ou expect.timeout).
  • Action Specific Timeout : La plupart des méthodes comme page.goto(), page.click(), page.waitForSelector() acceptent une option timeout.
const { chromium, TimeoutError } = require('playwright');

(async () => {
  const browser = await chromium.launch();
  const page = await browser.newPage();

  try {
    // Tenter d'aller sur une URL qui n'existe pas ou est très lente avec un timeout court
    await page.goto('http://non-existent-domain-very-slow.com', { timeout: 5000 });
    console.log('Navigué avec succès (ce ne devrait pas arriver).');
  } catch (error) {
    if (error instanceof TimeoutError) {
      console.error(`Erreur de Timeout: La navigation a pris trop de temps. Message: ${error.message}`);
    } else {
      console.error(`Autre erreur lors de la navigation: ${error.message}`);
    }
  }

  try {
    await page.goto('https://www.example.com');
    // Tenter de cliquer sur un élément qui n'apparaît pas dans les 2 secondes
    await page.click('#element-qui-n-existe-pas', { timeout: 2000 });
    console.log('Élément cliqué avec succès (ce ne devrait pas arriver).');
  } catch (error) {
    if (error instanceof TimeoutError) {
      console.error(`Erreur de Timeout: L'élément n'est pas apparu à temps. Message: ${error.message}`);
    } else {
      console.error(`Autre erreur lors du clic: ${error.message}`);
    }
  } finally {
    await browser.close();
  }
})();

B. Éléments Non Trouvés

Lorsqu'un sélecteur ne correspond à aucun élément, les méthodes comme page.click() ou page.locator().textContent() peuvent échouer. page.waitForSelector() est votre meilleur ami ici, car il attend l'élément et peut être géré par un try...catch en cas de timeout.

// Utilisation de waitForSelector
await page.goto('https://www.example.com');

const elementSelector = '#some-dynamic-element';
try {
  // Attend que l'élément soit visible pendant 10 secondes
  await page.waitForSelector(elementSelector, { state: 'visible', timeout: 10000 });
  const element = page.locator(elementSelector);
  await element.click();
  console.log('Élément cliqué avec succès.');
} catch (error) {
  if (error instanceof TimeoutError) {
    console.error(`L'élément '${elementSelector}' n'est pas apparu dans le délai imparti.`);
  } else {
    console.error(`Erreur inattendue lors de l'interaction avec l'élément : ${error.message}`);
  }
}

C. Assertions et Vérifications

Utilisez des bibliothèques d'assertion (comme expect de Playwright Test ou Jest) pour valider l'état de la page après des actions. Cela permet de s'assurer que l'opération a eu l'effet désiré et de lancer des erreurs claires si ce n'est pas le cas.

// Exemple avec expect de Playwright Test
const { test, expect } = require('@playwright/test');

test('should navigate to example.com and check title', async ({ page }) => {
  await page.goto('https://www.example.com');
  await expect(page).toHaveTitle('Example Domain');
});

test('should submit a form successfully', async ({ page }) => {
  await page.goto('https://www.example.com/form'); // Supposons une page de formulaire
  await page.fill('#username', 'testuser');
  await page.fill('#password', 'password123');
  await page.click('#submitButton');

  // Attendre une redirection et vérifier un message de succès
  await page.waitForURL('https://www.example.com/success');
  await expect(page.locator('#successMessage')).toHaveText('Form submitted successfully!');
});

2.3. Stratégies de Récupération

Lorsque des erreurs se produisent, comment rendre vos scripts plus résilients ?

A. Relances (Retries)

Pour les erreurs intermittentes (réseau, élément pas encore prêt), une simple relance peut suffire.

async function retry(fn, retries = 3, delay = 1000) {
  for (let i = 0; i < retries; i++) {
    try {
      return await fn();
    } catch (error) {
      console.warn(`Tentative ${i + 1}/${retries} échouée: ${error.message}. Nouvelle tentative dans ${delay}ms...`);
      if (i === retries - 1) throw error; // Rejeter si c'est la dernière tentative
      await new Promise(res => setTimeout(res, delay));
    }
  }
}

// Utilisation dans un script Playwright
(async () => {
  const { chromium } = require('playwright');
  const browser = await chromium.launch();
  const page = await browser.newPage();

  try {
    await page.goto('https://www.google.com'); // Naviguer d'abord pour avoir une page active

    await retry(async () => {
      // Simule une action qui échoue parfois
      const randomSuccess = Math.random() > 0.7; // 30% de chance de succès
      if (!randomSuccess) {
        throw new Error('Action intermittente échouée.');
      }
      await page.click('text="Gmail"'); // Tente de cliquer sur Gmail
      console.log('Action intermittente réussie!');
    }, 5, 2000); // 5 tentatives, 2 secondes d'attente entre chaque

  } catch (error) {
    console.error(`L'action a finalement échoué après plusieurs tentatives: ${error.message}`);
  } finally {
    await browser.close();
  }
})();

B. Mécanismes de Fallback

Si une approche échoue, essayez une autre. Par exemple, si un sélecteur ne fonctionne pas, essayez un sélecteur alternatif.

async function clickElementRobustly(page, selectors) {
  for (const selector of selectors) {
    try {
      await page.click(selector, { timeout: 5000 });
      console.log(`Clic réussi avec le sélecteur: ${selector}`);
      return true;
    } catch (error) {
      console.warn(`Échec du sélecteur '${selector}': ${error.message}. Tentative du prochain...`);
    }
  }
  throw new Error(`Aucun des sélecteurs n'a permis de cliquer sur l'élément.`);
}

// Utilisation
// await clickElementRobustly(page, ['#mainButton', '.alt-button', 'text="Click Me"']);

C. Nettoyage (Cleanup)

Assurez-vous toujours de fermer le navigateur ou la page, même en cas d'erreur. C'est là que le bloc finally est crucial.

const { chromium } = require('playwright');
let browser; // Déclarer en dehors pour être accessible dans finally

(async () => {
  try {
    browser = await chromium.launch();
    const page = await browser.newPage();
    await page.goto('https://www.example.com');
    await page.click('#nonExistentElement'); // Provoquer une erreur
  } catch (error) {
    console.error(`Erreur fatale: ${error.message}`);
  } finally {
    if (browser) {
      await browser.close();
      console.log('Navigateur fermé après exécution ou erreur.');
    }
  }
})();

3. Bonnes Pratiques pour des Scripts d'Automatisation Robustes et Maintenables

Pour aller au-delà de la simple correction d'erreurs, il faut adopter des pratiques qui préviennent les problèmes dès le départ et facilitent la maintenance.

3.1. Sélecteurs Robustes

C'est la règle d'or. Des sélecteurs fragiles sont la cause numéro un des échecs d'automatisation dus aux changements de l'UI.

  • À Éviter (fragile) :

    • Sélecteurs basés sur des classes CSS génériques (.button, .item).
    • Sélecteurs basés sur des attributs id générés dynamiquement (ex: id="app-12345").
    • Sélecteurs CSS longs basés sur la structure DOM (div > div > p:nth-child(2)).
  • À Préférer (robuste) :

    • data-test ou data-testid attributs : Idéal pour l'automatisation. Ils sont destinés à des fins de test et ne devraient pas changer souvent.
      <button data-test="submit-button">Envoyer</button>
      
      await page.click('[data-test="submit-button"]');
      
    • id uniques et stables : Si un id est garanti unique et ne change jamais.
      <input id="username-input" type="text">
      
      await page.fill('#username-input', 'monUtilisateur');
      
    • Attributs sémantiques : name, aria-label, role.
      <input name="email" type="email">
      <button role="button" aria-label="Envoyer le formulaire"></button>
      
      await page.fill('[name="email"]', 'test@example.com');
      await page.getByRole('button', { name: 'Envoyer le formulaire' }).click(); // Playwright
      
    • Sélecteurs basés sur le texte (Playwright : page.getByText(), Puppeteer : page.$x("//*[contains(text(), 'Mon Texte')]")) : Très utiles pour les éléments dont le texte est stable.
      await page.getByText('Se connecter').click();
      

3.2. Attentes Explicites vs. Implicites

  • Attentes Implicites (à éviter) : Utiliser des page.waitForTimeout(5000) (ou await new Promise(resolve => setTimeout(resolve, 5000))) est une mauvaise pratique. Vous attendez un temps fixe, ce qui rend le script lent s'il n'est pas nécessaire d'attendre aussi longtemps, ou fragile si le temps n'est pas suffisant.

  • Attentes Explicites (à privilégier) : Attendez que la condition réelle soit remplie.

    • page.waitForSelector(selector, { state: 'visible' }) : Attend qu'un élément soit visible.
    • page.waitForLoadState('networkidle') ou 'domcontentloaded' : Attend que la page soit chargée.
    • page.waitForURL(url) : Attend une navigation vers une URL spécifique.
    • page.waitForFunction(() => window.someVariable === 'ready') : Attend une condition JavaScript personnalisée.
// Mauvaise pratique
// await page.waitForTimeout(2000); // Ne fait pas confiance, attend juste un délai

// Bonne pratique
await page.waitForSelector('#dynamicContent', { state: 'visible' }); // Attend que le contenu apparaisse
await page.click('#submitButton');
await page.waitForURL('**/success'); // Attend la redirection vers la page de succès

3.3. Modularisation et Réutilisabilité (Page Object Model)

Organisez votre code pour éviter la duplication et améliorer la lisibilité. Le Page Object Model (POM) est une excellente approche :

  • Chaque "page" ou "composant" de votre application web a son propre fichier/objet qui encapsule les sélecteurs et les méthodes d'interaction.
  • Cela rend les scripts plus faciles à lire, à maintenir et à adapter si l'UI change.
// Exemple de Page Object Model (simplifié)
// pages/LoginPage.js
class LoginPage {
  constructor(page) {
    this.page = page;
    this.usernameInput = page.locator('#username');
    this.passwordInput = page.locator('#password');
    this.loginButton = page.locator('#loginButton');
  }

  async navigate() {
    await this.page.goto('/login'); // Assumant une base URL configurée
  }

  async login(username, password) {
    await this.usernameInput.fill(username);
    await this.passwordInput.fill(password);
    await this.loginButton.click();
  }
}

module.exports = { LoginPage };

// tests/login.test.js
const { test, expect } = require('@playwright/test');
const { LoginPage } = require('../pages/LoginPage');

test('should successfully log in a user', async ({ page }) => {
  const loginPage = new LoginPage(page);
  await loginPage.navigate();
  await loginPage.login('testuser', 'password123');

  // Vérifier la redirection ou un élément de la page d'accueil
  await expect(page).toHaveURL(/dashboard/);
  await expect(page.locator('#welcomeMessage')).toHaveText('Bienvenue, testuser!');
});

3.4. Gestion des Configurations et Variables d'Environnement

Ne codez jamais en dur des URLs, des identifiants ou d'autres paramètres sensibles.

  • Utilisez des variables d'environnement (process.env.MY_VAR).
  • Utilisez des fichiers de configuration (.env avec une bibliothèque comme dotenv).
  • Pour Playwright Test, le fichier playwright.config.ts est parfait pour les configurations de base.
// .env
BASE_URL=https://www.monapplication.com
ADMIN_USERNAME=admin
ADMIN_PASSWORD=securepassword
// script.js
require('dotenv').config(); // Charger les variables d'environnement

const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch();
  const page = await browser.newPage();

  await page.goto(process.env.BASE_URL || 'http://localhost:3000'); // Fallback si non défini

  // Utiliser les identifiants
  if (process.env.ADMIN_USERNAME && process.env.ADMIN_PASSWORD) {
    await page.fill('#username', process.env.ADMIN_USERNAME);
    await page.fill('#password', process.env.ADMIN_PASSWORD);
    await page.click('#loginButton');
  } else {
    console.warn('Variables d\'environnement pour l\'authentification manquantes.');
  }

  await browser.close();
})();

3.5. Journalisation (Logging)

Un bon système de journalisation est indispensable pour comprendre ce qu'un script a fait, surtout en cas d'échec ou d'exécution non-interactive.

  • Enregistrez les étapes importantes, les données d'entrée/sortie, et toutes les erreurs.
  • Utilisez des bibliothèques de logging plus sophistiquées comme winston ou pino pour des logs structurés et filtrables.
// Exemple de logging basique
const logger = {
  info: (message) => console.log(`[INFO] ${new Date().toISOString()} - ${message}`),
  warn: (message) => console.warn(`[WARN] ${new Date().toISOString()} - ${message}`),
  error: (message, error) => console.error(`[ERROR] ${new Date().toISOString()} - ${message}`, error),
};

// Utilisation
logger.info('Démarrage du processus de connexion...');
try {
  // ... votre code ...
  logger.info('Connexion réussie.');
} catch (err) {
  logger.error('La connexion a échoué.', err);
}

3.6. Tests Unitaires et d'Intégration (pour les utilitaires internes)

Bien que l'automatisation de navigateur soit souvent une forme de test d'intégration/E2E, les fonctions utilitaires au sein de vos scripts (manipulation de données, logique métier, wrappers personnalisés) peuvent et devraient être testées unitairement. Cela garantit la fiabilité de vos blocs de construction et réduit la complexité du débogage des interactions UI.


Conclusion

Félicitations ! Vous avez parcouru une leçon complète sur les piliers de l'automatisation de navigateurs robuste : le débogage, la gestion des erreurs et les bonnes pratiques.

Pour résumer, rappelez-vous ces points clés :

  • Le débogage est une compétence essentielle. Maîtrisez console.log, les points d'arrêt de votre IDE, et surtout les outils spécifiques à votre framework comme PWDEBUG=1 ou page.pause(). N'oubliez pas le mode non-headless (headless: false) et slowMo pour une observation visuelle.
  • La gestion des erreurs n'est pas une option. Utilisez try...catch pour anticiper les échecs, personnalisez les timeouts, et implémentez des stratégies de récupération comme les relances ou les fallbacks. Assurez toujours un nettoyage adéquat avec finally.
  • Les bonnes pratiques sont la fondation de scripts maintenables. Priorisez les sélecteurs robustes, utilisez des attentes explicites, structurez votre code avec le Page Object Model, gérez les configurations via variables d'environnement, et mettez en place une journalisation efficace.

Adopter une approche défensive dans votre programmation d'automatisation transformera vos scripts fragiles en des outils fiables et performants qui pourront résister aux caprices du web. Continuez à pratiquer, à expérimenter, et à affiner vos compétences. La robustesse est un chemin, pas une destination !