12k
All articles

Tests d'intégration vs tests de bout en bout

Tests dintégration vs tests de bout en bout pour les apps web : définitions claires, outils JS et quand utiliser Vitest, MSW, Playwright ou Cypress.

OpenReplay Team
OpenReplay Team
Tests d'intégration vs tests de bout en bout

Les tests d’intégration vérifient que vos propres composants et modules fonctionnent ensemble — ils s’exécutent en quelques millisecondes dans la CI et simulent les systèmes externes ; les tests de bout en bout pilotent l’application complète en cours d’exécution via la vraie interface utilisateur, comme le ferait un utilisateur, s’exécutent contre un build déployé ou de staging, et ne simulent rien. Cette seule distinction résout la plupart des confusions, mais elle masque un second problème propre au développement frontend : le mot « intégration » ne signifie pas la même chose pour un développeur JavaScript que pour un ingénieur QA backend, et la frontière entre « intégration » et « E2E » dans le navigateur est réellement floue.

Cet article trace cette frontière en termes de stack web. Il vous propose des définitions précises avec des exemples concrets, une comparaison selon les critères qui déterminent vraiment quel type de test écrire, la place de chacun par rapport aux tests unitaires, les outils JavaScript correspondant à chaque catégorie (Vitest, Jest, Testing Library, MSW, Playwright, Cypress), et une recommandation tranchée sur la façon de répartir votre budget de tests — pas le classique « utilisez les deux ».

Points clés à retenir

  • Les tests d’intégration rendent vos propres modules ensemble avec le réseau simulé et s’exécutent en quelques millisecondes dans la CI ; les tests de bout en bout pilotent l’application déployée dans un vrai navigateur et ne simulent rien.
  • Dans une application JavaScript, « intégration » signifie généralement Vitest ou Jest combiné à Testing Library et MSW ; « bout en bout » signifie généralement Playwright ou Cypress contre votre vraie URL.
  • La frontière intégration/E2E côté frontend est un spectre, pas un mur — l’endroit où vous la tracez est une décision budgétaire, pas une loi.
  • Écrivez principalement des tests d’intégration pour le meilleur rapport confiance/coût, maintenez une petite suite de trois à cinq tests E2E sur vos parcours à plus forte valeur, et n’essayez pas de tout couvrir en E2E.
  • Un bug en production est, par définition, un parcours que vos tests n’ont jamais vérifié, ce qui fait d’un replay de session d’un vrai échec le chemin le plus rapide vers le test manquant.

Tests d’intégration vs tests de bout en bout en un coup d’œil

Les deux types de tests diffèrent sur chaque axe qui compte pour la budgétisation : la couverture, la vitesse d’exécution, l’environnement d’exécution, ce qu’ils simulent, leur fréquence de rupture pour de mauvaises raisons, et les bugs que seuls ils peuvent détecter.

CritèreTest d’intégrationTest de bout en bout (E2E)
PérimètrePlusieurs de vos propres modules ensemble (ex. : un composant + sa couche de données)L’application complète en cours d’exécution, de bout en bout
VitesseQuelques millisecondes à quelques secondesQuelques secondes à plusieurs minutes par test
Environnement d’exécutionEn local et dans la CI, sous Node ou jsdomContre un build déployé ou de staging, dans un vrai navigateur
Ce qui est simuléLes systèmes externes — le réseau, les APIs tiercesRien (ou le moins possible)
InstabilitéFaible — pas de vrai réseau, pas de timing navigateur réelPlus élevée — dépend de la stabilité de l’ensemble du système
Coût / maintenancePlus faible à écrire et à maintenir au vertPlus élevé ; les suites se dégradent au fil des évolutions de l’application
Détecte uniquementContrats d’API rompus, réponses mal formées, câblage d’étatAuth, redirections, config d’environnement, scripts tiers, état inter-pages

Ce qu’est un test d’intégration, avec un exemple web

Un test d’intégration rend un arbre de vos propres composants ensemble et vérifie leur comportement combiné avec le réseau simulé, en s’assurant que les unités déjà couvertes par des tests unitaires coopèrent réellement. Il ne lance pas de navigateur et ne contacte pas de vrai serveur.

Un exemple concret : un composant LoginForm câblé à un hook d’authentification et à une récupération de profil. Le test d’intégration rend le formulaire, remplit les champs, soumet, et vérifie que l’état de bienvenue s’affiche — mais les appels POST /api/login et GET /api/profile sont interceptés et traités par un mock. Vous testez le câblage entre le formulaire, le hook, la couche de requêtes et le résultat rendu. Vous ne testez pas votre vrai backend, votre fournisseur d’authentification, ni votre CDN.

C’est à cette couche que les bugs de contrat font surface : le composant attend user.name mais le handler retourne user.fullName, un chemin 401 ne réaffiche jamais la bannière d’erreur, ou un indicateur de chargement ne se réinitialise jamais. Les tests d’intégration détectent les bugs qui vivent entre vos modules — contrats d’API rompus, réponses mal formées, câblage d’état — sans le coût d’un vrai déploiement.

Ce qu’est un test de bout en bout, avec un exemple web

Un test de bout en bout pilote votre application déployée dans un vrai navigateur, exactement comme le ferait un utilisateur, et vérifie ce que l’utilisateur voit — sans rien simuler. Il sollicite le vrai serveur, la vraie base de données, le vrai flux d’authentification, les vraies redirections, et tous les scripts tiers chargés par la page.

L’exemple canonique est un utilisateur qui se connecte et effectue un achat : naviguer vers l’URL en production, saisir les identifiants, soumettre, arriver sur le tableau de bord, ajouter un article au panier, finaliser le paiement et confirmer la commande. Chaque couche participe — le bundle frontend, le réseau, l’API, la base de données, les cookies de session, l’intégration de paiement.

Les tests E2E détectent les bugs qui n’existent que dans un déploiement réel : authentification et redirections, configuration d’environnement, scripts tiers, violations de politique de sécurité du contenu (CSP), et état inter-pages survivant à une navigation. Aucun de ces problèmes ne se reproduit lorsque le réseau est simulé, ce qui justifie précisément le coût plus élevé de l’E2E pour les parcours générateurs de revenus.

Leur place : la pyramide et le trophée

Les deux types de tests se situent au-dessus des tests unitaires, qui vérifient une seule fonction ou un seul composant de manière isolée. La classique pyramide de tests prescrit de nombreux tests unitaires à la base, moins de tests d’intégration au milieu, et très peu de tests E2E lents au sommet. C’est une valeur par défaut solide pour maintenir une suite rapide.

La contrepartie moderne est le trophée de tests, popularisé par Kent C. Dodds, qui épaissit la couche d’intégration parce que les tests d’intégration offrent le meilleur rapport confiance/coût pour les applications web — proches de l’utilisation réelle, mais sans l’instabilité et le temps d’exécution d’un E2E complet. Le raisonnement repose sur le principe directeur de Testing Library selon lequel plus vos tests ressemblent à la façon dont votre logiciel est utilisé, plus ils peuvent vous donner confiance. Un test d’intégration qui rend un vrai arbre de composants et vérifie un comportement visible par l’utilisateur ressemble bien plus à une utilisation réelle qu’un test unitaire d’un reducer isolé — et ce, en quelques millisecondes. Considérez le trophée comme une vision moderne largement adoptée pour les applications web, et non comme un remplacement universel de la pyramide.

La frontière floue côté frontend

Dans le développement frontend, la frontière intégration/E2E est un spectre, pas un mur : un test Testing Library qui rend un sous-arbre de composants est de l’« intégration », un test Playwright qui charge votre vraie URL est de l’« E2E », et l’endroit où vous tracez la ligne est une décision budgétaire, pas une loi. Cette frontière floue est la principale source de confusion, car le même mot signifie des choses différentes selon les interlocuteurs.

La littérature QA backend divise les tests d’intégration en stratégies bottom-up, top-down et big-bang — cette taxonomie est réelle, mais il s’agit de jargon d’intégration serveur, et elle aide rarement quelqu’un qui travaille avec Vitest et Playwright. Ignorez-la. Ce qui compte pour un développeur web, c’est la correspondance pratique : « intégration » signifie presque toujours rendre un arbre de composants avec le réseau simulé, et « E2E » signifie presque toujours piloter l’application déployée dans un vrai navigateur. La zone grise au milieu — par exemple, un test Playwright pointant vers un serveur de développement local avec MSW interceptant le réseau — est tout à fait acceptable ; décidez simplement délibérément de quel côté du budget elle provient.

La chaîne d’outils JS pour chaque type, avec du code

Dans une application JavaScript, un « test d’intégration » signifie généralement rendre un arbre de composants et vérifier son comportement avec le réseau simulé — typiquement Vitest ou Jest combiné à Testing Library et MSW — tandis qu’un « test de bout en bout » signifie piloter l’application déployée dans un vrai navigateur avec Playwright ou Cypress.

Mock Service Worker (MSW) est la pièce maîtresse qui rend les tests d’intégration réalistes sans vrai backend. Il intercepte les requêtes au niveau de la couche réseau dans les deux environnements — via l’API Service Worker dans le navigateur (setupWorker), et via l’interception de requêtes de bas niveau dans Node (setupServer, utilisé par vos tests Vitest ou Jest, sans nécessiter de fichier service worker). Parce que l’interception se produit à la frontière plutôt qu’en patchant fetch, les mêmes handlers alimentent vos tests d’intégration, votre serveur de développement, et même vos exécutions Playwright ; la documentation l’exprime directement : « Imaginez utiliser les mêmes mocks d’API pendant le développement, les tests d’intégration et de bout en bout, et dans votre Storybook. »

Voici la même fonctionnalité de connexion et récupération de profil sous forme de test d’intégration. Notez l’API MSW v2 http + HttpResponse, qui a remplacé le pattern res/ctx de la v1 :

// login.integration.test.tsx — Vitest 4 + @testing-library/react 16 + MSW 2
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { http, HttpResponse } from 'msw'
import { setupServer } from 'msw/node'
import { LoginForm } from './LoginForm'

const server = setupServer(
  http.post('/api/login', () =>
    HttpResponse.json({ token: 'abc' })
  ),
  http.get('/api/profile', () =>
    HttpResponse.json({ name: 'Ada' })
  )
)

beforeAll(() => server.listen())
afterEach(() => server.resetHandlers())
afterAll(() => server.close())

test('logs in and shows the profile name', async () => {
  render(<LoginForm />)
  await userEvent.type(screen.getByLabelText(/email/i), 'ada@example.com')
  await userEvent.type(screen.getByLabelText(/password/i), 'hunter2')
  await userEvent.click(screen.getByRole('button', { name: /sign in/i }))
  expect(await screen.findByText(/welcome, ada/i)).toBeInTheDocument()
})

Aucun navigateur ne se lance, aucun serveur ne tourne, et le réseau est entièrement contrôlé — ce test est donc rapide et déterministe. La même fonctionnalité sous forme de test E2E s’exécute contre l’application déployée et ne simule rien. Playwright pilote Chromium, Firefox et WebKit avec une seule API, avec attente automatique et assertions orientées web :

// login.e2e.spec.ts — Playwright 1.6x
import { test, expect } from '@playwright/test'

test('user logs in and sees their profile', async ({ page }) => {
  await page.goto('/login')
  await page.getByLabel('Email').fill('ada@example.com')
  await page.getByLabel('Password').fill('hunter2')
  await page.getByRole('button', { name: 'Sign in' }).click()
  await expect(
    page.getByRole('heading', { name: /welcome, ada/i })
  ).toBeVisible()
})

Les assertions se ressemblent ; ce qui diffère, c’est tout ce qui se passe en dessous. La spec Playwright prouve que le vrai endpoint d’authentification, la redirection et le cookie de session fonctionnent dans un vrai navigateur — et elle paie cette preuve en temps d’exécution et en instabilité. Une limite pratique à connaître : les React Server Components asynchrones étant récents, Jest ne les supporte pas encore et Next.js recommande des tests E2E pour les composants asynchrones — un cas concret où la couche d’intégration ne peut pas encore atteindre et où l’E2E justifie sa place.

Une règle de décision par scénario

Choisissez le type de test en fonction du bug que vous cherchez à prévenir, pas par habitude. La règle :

  • Nouvelle interaction entre modules ou contrat frontend-backend → test d’intégration. Câbler un composant à un nouvel endpoint, un nouveau reducer à un nouveau selector, un formulaire à une couche de validation : rendez l’arbre, simulez le réseau avec MSW, vérifiez le comportement.
  • Un parcours utilisateur critique sur toute la stack → un test E2E. Connexion, inscription, paiement, le parcours qui, s’il est cassé, coûte de l’argent ou de la confiance.
  • Tout le reste → misez sur l’intégration, et n’en faites pas un E2E.

La recommandation pratique pour une équipe web : écrivez principalement des tests d’intégration pour le meilleur rapport confiance/coût, maintenez une petite suite de trois à cinq tests de bout en bout sur vos parcours à plus forte valeur, et n’essayez pas de tout couvrir en E2E. Un test E2E pour une infobulle est une charge de maintenance ; un test d’intégration pour celle-ci est une assurance bon marché.

Pourquoi les suites E2E se dégradent, et comment les maintenir stables

Les suites de tests E2E se dégradent parce que chaque test dépend de la stabilité de l’ensemble du système ; la solution est de les garder peu nombreux, de vérifier les résultats visibles par l’utilisateur plutôt que les détails d’implémentation, et de supprimer ceux qui ne protègent plus aucun revenu. Un identifiant de test renommé, un déploiement de staging plus lent, un widget tiers instable, ou une redirection modifiée peuvent faire passer une suite au rouge sans aucune vraie régression — et une suite qui crie au loup finit par être ignorée. Concrètement :

  • préférez les sélecteurs basés sur les rôles et les labels plutôt que les chemins CSS fragiles ;
  • fiez-vous à l’attente automatique de Playwright plutôt qu’à des délais fixes ;
  • isolez les données de test ; et
  • limitez le nombre de tests pour que la suite reste suffisamment rapide pour être fiable.

Aucune suite ne couvre les parcours que vous n’avez pas imaginés, cependant — et un bug en production est, par définition, un chemin que vos tests d’intégration et E2E n’ont jamais vérifié. Le replay de session prend tout son sens précisément ici : un replay d’un vrai échec montre le parcours non testé exact qui a échoué — l’état du DOM, les appels réseau et les erreurs console au moment de l’échec. Les replays de session des flux de connexion et de paiement révèlent fréquemment des modes d’échec qu’aucun test local n’avait exercés, comme une redirection spécifique à un environnement ou un script tiers bloqué par la CSP. Le geste professionnel est de transformer cette reproduction en test manquant : une rupture au niveau du parcours devient une nouvelle spec Playwright, une rupture de composant ou de contrat devient un nouveau test d’intégration Testing Library + MSW, et le même bug ne peut plus régresser.

Conclusion

La répartition qui tient en production, c’est principalement des tests d’intégration, quelques E2E à forte valeur, et des tests unitaires en dessous pour la logique pure — l’intégration parce qu’elle offre le meilleur rapport confiance/temps d’exécution, l’E2E parce que certains bugs n’existent que dans un vrai navigateur déployé. Commencez par écrire des tests d’intégration avec Vitest ou Jest, Testing Library et MSW pour les frontières de modules de votre prochaine fonctionnalité, réservez Playwright pour la poignée de parcours que vous ne pouvez pas vous permettre de casser, et laissez les vrais échecs en production vous indiquer quel test vous avez oublié d’écrire.

FAQ

Un seul outil comme Playwright ou Cypress peut-il écrire à la fois des tests d'intégration et des tests de bout en bout ?

Oui, mais la distinction vient de la façon dont vous délimitez le test, pas de l'outil. Playwright et Cypress peuvent monter un composant de manière isolée avec le réseau simulé, ce qui constitue effectivement un test d'intégration, ou charger votre URL déployée et exercer toute la stack, ce qui est du bout en bout. Des modes de test de composants existent dans les deux, tandis que Vitest ou Jest avec Testing Library reste le chemin d'intégration le plus courant dans l'écosystème JavaScript.

Ai-je besoin du fichier mockServiceWorker.js pour MSW dans les tests d'intégration Vitest ou Jest ?

Non. Le fichier mockServiceWorker.js n'est requis que pour le chemin navigateur utilisant setupWorker, qui s'appuie sur l'API Service Worker. Les tests d'intégration basés sur Node passent par setupServer, qui intercepte les requêtes via une extension de classe de bas niveau plutôt qu'un service worker, donc aucun fichier généré n'est nécessaire. Vous ne générez le script worker que lorsque vous simulez des requêtes dans un vrai navigateur, par exemple pendant le développement local ou des exécutions de tests dans le navigateur.

Quand devrais-je écrire un test de bout en bout plutôt qu'un test d'intégration ?

Écrivez un test de bout en bout lorsque le bug que vous cherchez à prévenir n'existe que dans un déploiement réel — authentification, redirections, configuration d'environnement, cookies de session, scripts tiers, violations de politique de sécurité du contenu (CSP), ou état inter-pages. Ces problèmes ne se reproduisent jamais lorsque le réseau est simulé. Pour tout ce qui se passe entre vos propres modules, comme les contrats d'API rompus, les réponses mal formées ou le câblage d'état, un test d'intégration est plus rapide, moins coûteux et moins instable. Réservez les tests de bout en bout à vos parcours générateurs de revenus à plus forte valeur.

Pourquoi mes tests de bout en bout passent-ils en local mais échouent-ils dans la CI ?

Les suites E2E dépendent de la stabilité de l'ensemble du système, donc les différences entre les environnements local et CI les font échouer sans aucune vraie régression. Les causes courantes incluent les délais fixes au lieu de l'attente automatique, les sélecteurs CSS fragiles qui changent entre les builds, les délais de déploiement plus lents dans la CI, les données de test partagées ou non isolées, et les widgets tiers instables. Préférez les sélecteurs basés sur les rôles et les labels, fiez-vous à l'attente automatique de Playwright, isolez les données de test par exécution, et gardez la suite suffisamment petite pour rester fiable.

Understand every bug

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — self-hosted, with full data ownership.

Star on GitHub

We use cookies to improve your experience. By using our site, you accept cookies.