Tests E2E : pourquoi votre suite SaaS est lente et fragile

par Benjamin

Votre suite de tests end-to-end (E2E) tourne pendant 25 minutes. Elle échoue une fois sur cinq. Quand elle passe au vert, personne ne sait vraiment ce qu'elle protège.

Alors on augmente les timeouts. On ajoute des retries. On relance le pipeline. Et on finit par considérer les tests comme une taxe inévitable de l'équipe technique.

Ce n'est pas une fatalité. Le problème n'est généralement pas l'outil - Playwright, Cypress ou un autre. C'est une suite qui teste trop de détails, pas assez de parcours critiques, et qui dépend d'un environnement qu'elle ne contrôle pas.

Un test E2E doit répondre à une seule question

Un test E2E simule un utilisateur à travers l'interface et la pile applicative complète : navigateur, frontend, API, base de données et services nécessaires au parcours. Il répond à une question métier concrète : un utilisateur peut-il encore accomplir cette action importante ?

Cette portée le rend précieux, mais coûteux. Plus un test traverse de couches, plus il est lent à exécuter et plus il a de dépendances susceptibles de varier. Le test E2E n'est donc pas le bon endroit pour vérifier chaque règle métier, chaque branche conditionnelle ou chaque composant visuel.

La règle est simple : utilisez les tests E2E pour les parcours à forte valeur et les contrats entre les couches. Laissez les tests unitaires et d'intégration couvrir le reste.

Le symptôme le plus dangereux : le test flaky

Un test flaky réussit ou échoue sans modification du code testé. Il crée deux dégâts : il fait perdre du temps à l'équipe, et il détruit progressivement la confiance dans le pipeline.

Quand un échec est ignoré parce que "c'est sûrement le test", un vrai bug peut passer derrière. Une suite rouge qui n'est plus prise au sérieux ne protège plus rien.

Les causes reviennent souvent :

  • Des sélecteurs couplés au HTML ou aux classes CSS. Un détail de présentation change, le test casse alors que le parcours utilisateur fonctionne toujours.
  • Des attentes temporelles arbitraires. waitForTimeout(2000) ne synchronise pas le test avec l'état réel de l'application. Il ajoute seulement une durée fixe, trop courte dans certains cas et inutilement longue dans d'autres.
  • Des données partagées entre les tests. L'ordre d'exécution ou le parallélisme modifie l'état laissé par un autre test.
  • Des dépendances externes non maîtrisées. Une API de paiement, un fournisseur d'identité ou un service tiers tombe, et votre suite signale un défaut qui n'est pas dans votre code.
  • Des assertions trop faibles ou trop techniques. Vérifier qu'une classe CSS existe ne prouve pas qu'un utilisateur a pu terminer son action.

Un retry peut masquer un symptôme. Il ne corrige aucune de ces causes.

La stratégie qui fonctionne : peu de parcours, mais les bons

Je préfère une petite suite E2E lisible à une collection exhaustive que personne n'entretient. Pour la construire, je pars des parcours qui peuvent réellement bloquer le produit :

  1. Identifier les actions vitales. Pour un SaaS, cela peut être créer un compte, se connecter, créer ou modifier la ressource principale, inviter un membre, souscrire à une offre ou exporter une donnée.
  2. Définir le résultat observable. L'utilisateur voit quoi quand le parcours réussit ? Une page, un message, une ligne dans une liste, un email déclenché ? L'assertion doit porter sur ce résultat, pas sur l'implémentation.
  3. Tester le chemin nominal. Un test E2E doit d'abord confirmer que le parcours normal fonctionne de bout en bout. Les variantes et erreurs importantes viennent ensuite.
  4. Attribuer les règles aux bons niveaux. Une règle de calcul n'a pas besoin d'être vérifiée dans dix scénarios navigateur. Testez-la près du code, puis gardez un parcours E2E qui confirme que le résultat arrive bien à l'utilisateur.
  5. Supprimer les doublons. Si cinq tests passent par la même connexion et vérifient la même navigation, ils ne donnent pas cinq fois plus de confiance. Ils donnent cinq fois plus de maintenance.

La couverture utile ne se mesure pas au nombre de tests. Elle se mesure au risque métier couvert.

Des sélecteurs qui résistent aux refontes

Un test doit interagir avec l'interface comme un utilisateur la perçoit. Les rôles accessibles, les labels et le texte visible sont de meilleurs contrats que la structure interne du DOM.

Avec Playwright, un sélecteur comme getByRole('button', { name: 'Enregistrer' }) exprime une intention. Un sélecteur comme .btn-primary:nth-child(2) exprime une coïncidence de mise en page.

Ce choix a une conséquence intéressante : il rend aussi les tests utiles pour l'accessibilité. Si un bouton n'a pas de nom accessible, le test peut révéler un problème de produit plutôt qu'obliger l'équipe à ajouter un identifiant technique uniquement pour l'automatisation.

Les sélecteurs dédiés restent pertinents quand l'interface ne fournit pas de contrat utilisateur stable. Dans ce cas, utilisez un data-testid explicite et considéré comme une API de test, pas un chemin CSS fragile.

Chaque test doit repartir d'un état connu

Un test isolé doit pouvoir être lancé seul, dans n'importe quel ordre, sans dépendre du résultat d'un test précédent. C'est une contrainte de conception, pas une option de confort.

Pour y parvenir :

  • créez des données propres au test, avec un identifiant unique si nécessaire ;
  • préparez les états lourds par API ou par fixture plutôt qu'en cliquant à chaque fois sur dix écrans ;
  • réinitialisez ou recréez les données après le test ;
  • utilisez un environnement de staging contrôlé, qui ne change pas au milieu de la suite ;
  • ne faites pas dépendre un test de données produites par un service tiers en temps réel.

Le navigateur doit vérifier le parcours. Il ne doit pas être votre seul outil pour fabriquer tout le contexte du test.

Maîtriser les services externes sans mentir sur la couverture

Une suite E2E qui appelle réellement un service de paiement à chaque exécution teste autant la disponibilité de ce fournisseur que votre application. Ce n'est pas forcément inutile, mais ce n'est pas le même test.

Pour les tests fréquents, je préfère intercepter les appels aux services externes et fournir des réponses déterministes. On vérifie ainsi que notre application réagit correctement à un succès, un refus ou une erreur connue. Un petit nombre de tests séparés peut ensuite vérifier l'intégration réelle, dans un environnement prévu pour cela.

La frontière doit rester explicite : simuler Stripe ne prouve pas que Stripe est disponible. Cela prouve que votre code traite correctement le contrat que vous avez défini avec Stripe. Cette distinction évite de sur-vendre ce que la suite garantit.

Les retries ne sont pas une stratégie de qualité

Un retry est utile pour diagnostiquer un problème transitoire ou récupérer une instabilité exceptionnelle d'infrastructure. Il devient dangereux quand il sert à rendre un test flaky acceptable.

La bonne séquence est :

  1. conserver la trace de l'échec initial ;
  2. identifier la cause, par exemple donnée partagée, attente mal synchronisée ou service non maîtrisé ;
  3. corriger la cause ;
  4. utiliser le retry comme filet de sécurité, jamais comme preuve que le test est fiable.

Si un test échoue régulièrement au premier passage mais réussit au second, le pipeline doit le rendre visible. Sinon, vous transformez un signal de qualité en bruit opérationnel.

Le pipeline doit donner un signal exploitable

Une suite E2E bien conçue ne se contente pas de produire une coche verte ou rouge. En cas d'échec, elle doit permettre de comprendre rapidement : quel parcours, avec quelles données, dans quel environnement, sur quelle étape.

Je vérifie au minimum :

  • le nom du parcours dans le titre du test ;
  • une capture ou une trace à l'échec ;
  • les logs et la réponse des requêtes utiles ;
  • la conservation de l'état nécessaire au diagnostic, sans exposer de données sensibles ;
  • la distinction entre échec produit, échec d'environnement et indisponibilité d'une dépendance.

Playwright recommande notamment les assertions qui attendent l'état attendu, l'isolation des tests, les locators orientés utilisateur et les traces pour diagnostiquer les échecs en CI. Ce sont des pratiques de conception, pas des réglages magiques à appliquer après coup.

Une grille de décision pour savoir quoi tester

Avant d'ajouter un test E2E, je pose quatre questions :

  • Le parcours est-il critique pour le business ou pour un utilisateur ? Si non, un test plus local suffit peut-être.
  • Le comportement traverse-t-il plusieurs couches que les tests unitaires ne peuvent pas relier ? Si non, ne payez pas le coût du navigateur.
  • Le résultat attendu est-il observable et stable ? Si non, clarifiez d'abord le contrat produit.
  • L'environnement et les données sont-ils contrôlables ? Si non, le test sera difficile à interpréter.

Trois réponses positives ne garantissent pas un bon test, mais une réponse négative est souvent un signal pour changer de niveau de test ou améliorer le contexte avant d'écrire le scénario.

FAQ : tests E2E pour une application SaaS

Quelle est la différence entre un test E2E et un test d'intégration ?

Un test E2E vérifie un parcours à travers l'interface et la pile applicative complète, comme le ferait un utilisateur. Un test d'intégration vérifie la collaboration entre plusieurs composants ou systèmes à un niveau plus ciblé, par exemple une API avec une base de données. Les deux sont utiles, mais leur coût et leur périmètre sont différents.

Combien de tests E2E faut-il pour un SaaS ?

Il n'existe pas de nombre universel. Commencez par les parcours critiques qui peuvent empêcher un utilisateur de s'inscrire, d'utiliser la fonction principale, de collaborer, de payer ou de récupérer ses données. Ajoutez ensuite des scénarios selon les risques observés, pas pour atteindre un quota de tests.

Comment réduire les tests E2E instables ?

Utilisez des données isolées, des attentes sur l'état réel plutôt que des délais fixes, des sélecteurs orientés utilisateur et des dépendances externes contrôlées. Analysez toujours les échecs avant d'augmenter les retries, car un retry ne corrige pas une cause d'instabilité.

Playwright est-il obligatoire pour les tests E2E ?

Non. Playwright est un outil adapté à de nombreux projets, mais la stratégie compte davantage que le framework. Cypress, Selenium ou un autre outil peuvent convenir. Le critère principal est la capacité à produire des tests lisibles, isolés, diagnostiquables et alignés sur les parcours critiques.

Faut-il tester chaque fonctionnalité avec le navigateur ?

Non. Le navigateur est le niveau le plus coûteux. Les règles métier et les cas détaillés doivent être testés au niveau unitaire ou intégration quand c'est possible. Gardez les tests E2E pour vérifier que les parcours importants fonctionnent réellement de bout en bout.

Ce que je recommande, en tant que CTO hands-on

Une suite E2E fiable n'est pas celle qui contient le plus de scénarios. C'est celle que l'équipe croit quand elle devient rouge, et qu'elle peut corriger sans passer la journée à deviner.

En mission de structuration technique, je commence par cartographier les parcours critiques, identifier les tests qui doublonnent ou mentent, puis remettre de l'ordre dans les niveaux de vérification. Le but n'est pas d'ajouter une couche de tests pour donner bonne conscience. Le but est de réduire le risque de régression avec un signal suffisamment rapide et précis pour être utilisé au quotidien.

Vos tests E2E ne doivent pas simuler toute l'entreprise. Ils doivent répondre à quelques questions essentielles : un utilisateur peut-il encore faire ce qui fait vivre le produit ?


Sources :

Benjamin Raimond

Benjamin Raimond

CTO hands-on : j'architecture, je code, je structure l'équipe technique. Je transforme des objectifs business en solutions robustes et durables pour les startups et PME.

En savoir plus →