Cadre de conception d'applications axé sur l'automatisation et meilleures pratiques

Un concept promouvant la manière dont les développeurs peuvent concevoir leurs applications pour qu'elles soient compatibles avec l'automatisation dès le premier jour – le point de vue d'un testeur.

Introduction

En tant que testeur d'automatisation, j'ai passé d'innombrables heures à me débattre avec des applications qui semblaient conçues délibérément pour résister à l'automatisation. Des sélecteurs fragiles qui dysfonctionnent au moindre changement d'interface, des composants sans propriétés identifiables et des flux de travail complexes dissimulés derrière des états obscurs : voilà les frustrations quotidiennes auxquelles nous sommes confrontés.

Mais voilà le point essentiel : la plupart de ces problèmes sont évitables. Lorsque les développeurs conçoivent des applications en intégrant l’automatisation dès le départ, les tests deviennent plus rapides, plus fiables et bien plus faciles à maintenir.

Il ne s'agit pas seulement de nous faciliter la tâche, mais aussi de fournir plus rapidement des logiciels de meilleure qualité.

Cet article partage des recommandations pratiques que tout développeur d'applications devrait suivre pour rendre ses applications « prêtes pour l'automatisation ».

Rendre les éléments identifiables et stables

Le problème:

De nombreuses applications s'appuient exclusivement sur les noms de classes CSS ou sur des identifiants générés qui changent à chaque compilation. Les frameworks d'automatisation comme Selenium, Playwright et Appium utilisent des localisateurs (identifiants uniques) pour interagir avec les éléments.

Ce que les développeurs devraient faire :

  • Ajouter data-testid attributs des éléments interactifs (boutons, champs de saisie, formulaires, liens)
  • Veillez à ce que ces identifiants restent stables d'une version à l'autre — traitez-les comme des API publiques.
  • Évitez de vous fier uniquement aux noms de classes dynamiques ou aux positions XPath

Exemple réel :

// ❌ BAD - Changes frequently 

<button className="btn-primary-v2 btn-submit-form"> 

Submit 

</button> 

// ✅ GOOD - Stable identifier for testers 

<button data-testid="form-submit-button" className="btn-primary-v2"> 

Submit 

</button>

SOUSCRIVEZ

Pourquoi cela compte:

Lorsque vous ajoutez data-testid, les testeurs peuvent localiser les éléments de manière fiable en utilisant page.locator('[data-testid="form-submit-button"]') en dramaturge ou find_element(By.CSS_SELECTOR, '[data-testid="form-submit-button"]') dans Selenium.

Utilisez le HTML sémantique et des associations d'étiquettes appropriées.

Le problème:

Générique <div> et <span> et les éléments sans étiquettes rendent presque impossible pour les testeurs de comprendre la fonction des contrôles et pour les outils d'accessibilité de fonctionner correctement.

Ce que les développeurs devraient faire :

  • Utilisez des éléments HTML sémantiques : <button>, <input>, <label>, <form>
  • Associer des étiquettes aux entrées en utilisant <label for="inputId">
  • Utilisez le aria-label or aria-labelledby pour les composants complexes

Exemple réel :

// ❌ BAD - No semantic meaning 

<div onClick={() => setSelected(!selected)}> 

<span>Enable notifications</span> 

</div> 

 

// ✅ GOOD - Semantic and testable 

<label htmlFor="notifications-toggle"> 

<input  

id="notifications-toggle"  

type="checkbox"  

data-testid="notifications-toggle" 

/> 

Enable notifications 

</label>

Point de vue du testeur :

Avec le HTML sémantique, je peux utiliser des sélecteurs basés sur les rôles : page.locator('input[role="checkbox"]') ou même getByRole("checkbox", { name: /enable notifications/i }) dans Playwright, ce qui rend les tests plus lisibles et plus résistants aux changements de style.

Implémenter des états d'attente explicites et des indicateurs de chargement

Le problème:

Les applications utilisant beaucoup de JavaScript chargent leur contenu de manière dynamique. Les testeurs rencontrent souvent des problèmes de stabilité car les éléments apparaissent et disparaissent de façon asynchrone, et ils ne savent pas quand la page est réellement « prête ».

Ce que les développeurs devraient faire :

  • Utilisez le data-testid pour les états de chargement :
    <div data-testid="loading-spinner"> 
  • Marquer les sections de contenu avec des attributs de statut : aria-busy="true" pendant la charge
  • Assurez-vous que l'application atteigne un état stable avant de répondre aux interactions des utilisateurs.
  • Fournir des signaux explicites de fin de chargement

Exemple réel :

// ❌ BAD - No semantic meaning 

<div onClick={() => setSelected(!selected)}> 

<span>Enable notifications</span> 

</div> 

 

// ✅ GOOD - Semantic and testable 

<label htmlFor="notifications-toggle"> 

<input  

id="notifications-toggle"  

type="checkbox"  

data-testid="notifications-toggle" 

/> 

Enable notifications 

</label>

Code d'automatisation du testeur (auteur dramatique) :

// Wait for loading to complete 

await page.locator('[data-testid="loading-spinner"]').waitFor({ state: 'hidden' }); 

 

// Now interact with the list 

const userList = page.locator('[data-testid="user-list"]'); 

await expect(userList).toBeVisible();

Concevoir des formulaires avec une gestion claire des entrées 

Le problème: 

Les formulaires complexes présentant des états d'erreur peu clairs, des validations cachées ou des comportements de saisie non standard perturbent les scripts d'automatisation. 

Ce que les développeurs devraient faire: 

  • Utilisez les éléments de formulaire HTML standard avec des attributs de nom clairs. 
  • Fournir des messages d'erreur explicites avec data-testid marqueurs 
  • S'assurer que la validation du formulaire est observable (et non pas des échecs silencieux). 
  • Inclure les confirmations de succès 

Exemple réel: 

// ✅ GOOD - Clear form with observable states 

<form data-testid="login-form"> 

<div> 

<label htmlFor="email-input">Email:</label> 

<input 

id="email-input" 

name="email" 

type="email" 

data-testid="email-input" 

required 

/> 

{errors.email && ( 

<span data-testid="email-error" role="alert"> 

{errors.email} 

</span> 

)} 

</div> 

 

<div> 

<label htmlFor="password-input">Password:</label> 

<input 

id="password-input" 

name="password" 

type="password" 

data-testid="password-input" 

required 

/> 

</div> 

 

<button  

type="submit"  

data-testid="login-submit-button" 

disabled={isSubmitting} 

> 

{isSubmitting ? 'Logging in...' : 'Login'} 

</button> 

 

{submitSuccess && ( 

<div data-testid="login-success-message" role="status"> 

Login successful! Redirecting... 

</div> 

)} 

</form>

Code d'automatisation du testeur (Selenium) :

from selenium import webdriver 

from selenium.webdriver.common.by import By 

from selenium.webdriver.support.ui import WebDriverWait 

from selenium.webdriver.support import expected_conditions as EC 

 

driver = webdriver.Chrome() 

driver.get("https://app.com/login") 

 

# Fill form 

driver.find_element(By.CSS_SELECTOR, '[data-testid="email-input"]').send_keys("test@example.com") 

driver.find_element(By.CSS_SELECTOR, '[data-testid="password-input"]').send_keys("password123") 

 

# Submit 

driver.find_element(By.CSS_SELECTOR, '[data-testid="login-submit-button"]').click() 

 

# Wait for success message 

WebDriverWait(driver, 10).until( 

EC.presence_of_element_located((By.CSS_SELECTOR, '[data-testid="login-success-message"]')) 

)

Rendre les réponses de l'API prévisibles et cohérentes

Le problème:

Les applications mobiles et web qui dépendent des réponses d'API nécessitent des structures de données stables. Des réponses d'API incohérentes ou imprévisibles entraînent des échecs de tests aléatoires.

Ce que les développeurs devraient faire :

  • Utilisez une structure JSON cohérente sur tous les points de terminaison.
  • Incluez des identifiants uniques (ID) dans chaque objet
  • Contrats d'API de documentation (OpenAPI/Swagger)
  • Renvoyer des codes d'état HTTP et des messages d'erreur pertinents
  • Tester les réponses de l'API pendant le développement

Exemple réel :

// ✅ GOOD - Predictable API response 

{ 

"success": true, 

"data": { 

"id": "user-123", 

"email": "test@example.com", 

"name": "John Doe", 

"created_at": "2026-03-01T10:00:00Z" 

}, 

"timestamp": "2026-03-22T14:30:00Z" 

} 

 

// ❌ BAD - Inconsistent response 

{ 

"status": "ok", 

"user": { 

"uid": "123", 

"email_address": "test@example.com" 

}, 

"meta": { "time": 1679574600 } 

}

Automatisation mobile du testeur (Appium) :

# With predictable API, testers can mock and validate 

from appium import webdriver 

 

driver = webdriver.Remote("http://localhost:4723/wd/hub", capabilities) 

 

# Mock the API response 

mock_response = { 

"success": True, 

"data": { 

"id": "user-123", 

"name": "Test User" 

} 

} 

 

# Now reliably access user data in the app 

user_name_element = driver.find_element("xpath", "//text[@value='Test User']")

Fournir une configuration et des modes adaptés aux tests

Le problème:

Les fonctionnalités de production telles que les outils d'analyse, les publicités, les traqueurs tiers et les délais réseau peuvent interférer avec l'exécution des tests.

Ce que les développeurs devraient faire :

  • Indiquez le mode de test ou une variable d'environnement
  • Autoriser la désactivation des analyses, des publicités et du suivi dans les environnements de test
  • Prise en charge du saut des animations ou délais inutiles
  • Fournir des points de terminaison d'API pour réinitialiser l'état de l'application à des fins de test

Exemple concret (React) :

// ✅ GOOD - Test-friendly configuration 

const isTestMode = process.env.REACT_APP_TEST_MODE === 'true'; 

 

function App() { 

return ( 

<div> 

{!isTestMode && <Analytics />} 

{!isTestMode && <Ads />} 

<MainContent /> 

</div> 

); 

}

Variable d'environnement :

REACT_APP_TEST_MODE=true npm start

Attentes en matière d'automatisation des documents

Le problème:

Les testeurs consacrent du temps à analyser en profondeur comment interagir avec une application, notamment avec ses composants personnalisés.

Ce que les développeurs devraient faire :

  • Documents data-testid conventions de nommage dans votre fichier README
  • Fournir un guide de test/automatisation pour les fonctionnalités complexes
  • Documentation du composant partagé (Storybook, etc.)
  • Listez tous les événements personnalisés et les rappels que les testeurs doivent connaître.

Exemple de documentation :

## Automation Testing Guide 

 

### Naming Conventions 

- Buttons: `data-testid="[feature]-[action]-button"` (e.g., `user-logout-button`) 

- Form inputs: `data-testid="[form]-[field]-input"` (e.g., `login-email-input`) 

- Lists: `data-testid="[list]-container"` and items as `[list]-item-[id]` 

 

### Key Components 

- **Modal**: `.modal-overlay` (hidden when closed), contains `data-testid="modal-close-button"` 

- **Dropdown**: Use semantic `<select>` or `aria-haspopup="listbox"` 

- **Date Picker**: Supports keyboard navigation and `aria-label` 

 

### Test Environment Setup 

Set `API_URL=http://localhost:3001/api` to use mock server

Points clés à retenir pour les développeurs

Ce dont les testeurs ont besoin Ce que les développeurs devraient faire
Identifiants d'éléments stables Utilisez les attributs data-testid
Hiérarchie des composants claire Utilisez du HTML sémantique
États de chargement/prêt observables Fournir des indicateurs de chargement avec des identifiants
Comportement de forme prévisible Éléments standard de validation et d'utilisation des documents
Réponses API cohérentes Respectez les contrats API et la structure des documents.
Environnement propice aux tests Mode test pris en charge, désactivation des publicités et des analyses
Des conseils clairs en matière d'automatisation Documentez vos attentes en matière d'automatisation

Conclusion

Créer des applications prêtes pour l'automatisation ne consiste pas à contourner les limitations, mais à concevoir correctement les applications dès le premier jour.

Lorsque les développeurs privilégient la clarté, la cohérence et l'observabilité, tout le monde y gagne : les testeurs écrivent des tests plus rapides et plus fiables ; les utilisateurs bénéficient de logiciels de meilleure qualité et toute l'équipe avance plus vite.

La bonne nouvelle ? La plupart de ces pratiques sont de toute façon de bonnes pratiques d’ingénierie logicielle. Un HTML sémantique, des API prévisibles et une gestion claire des erreurs améliorent l’accessibilité, les performances et l’expérience utilisateur, et pas seulement les tests automatisés.

Préparer : data-testid Utilisez des attributs HTML sémantiques, affichez les états de chargement et documentez vos composants. Vos testeurs d'automatisation vous en remercieront et votre application n'en sera que meilleure.

Ressources et outils

Vous aimerez aussi