Tests end-to-end : comment démarrer du bon pied

Les tests end-to-end doivent être utilisés avec parcimonie, car ils sont coûteux à écrire (Page Model) et coûteux à exécuter. Voici comment procéder.
Publié le 8 janvier 2025Dernière mise à jour le 14 février 2025

Définition

Les tests end-to-end (e2e) reproduisent le comportement d'un utilisateur interagissant avec l'application que l'on souhaite tester. Le terme peut être compris de deux manières complémentaires, toutes deux utiles :
  • En largeur : puisqu'on simule le comportement d'un utilisateur, on veut être au plus près de son expérience réelle (sa User Journey). Cela signifie reproduire un cas d'usage réel et complet, partant de l'action que souhaite réaliser l'utilisateur (Job To Be Done), par opposition à un test unitaire, qui se concentre sur un comportement local de l'application. Exemple d'un site e-commerce : de la consultation anonyme du catalogue jusqu'au paiement réussi.
  • En épaisseur : on souhaite que le test "traverse" le maximum de couches applicatives, de l'IHM (nécessairement) à la base de données (idéalement). Cela nous conforte dans l'idée que ces couches dialoguent bien les unes avec les autres. C'est donc utile, mais c'est aussi quasiment nécessaire dès que l'on fait de vrais tests de bout à bout, en largeur, parce qu'il devient très difficile de mocker.
Voilà, donc pour faire simple, on souhaite mocker le moins possible.

Utilisation d'un attribut personnalisé

Un framework de tests e2e agit sur le DOM ou lit sur le DOM à l'aide de sélecteurs. Ces sélecteurs peuvent reposer sur la structure HTML ou bien sur les noms de classes, mais une pratique courante consiste à l'aider un peu en définissant un attribut personnalisé (custom attribute),
data-test-id
par exemple. En effet, cette dernière solution est celle qui couple le moins vos tests à l'implémentation de l'application.
Pour rappel, on veut que vos tests échouent seulement parce que le comportement de l'application a changé. On ne veut pas que les tests échouent parce que l'implémentation de l'application a changé, à comportement égal.

Création d'un Page Model

Lorsqu'on écrit des tests e2e, on souhaite en masquer au maximum l'implémentation, de manière à les rendre lisibles par des humain (miam les
cy.get(selector('guess')).eq(count - 1).find(selector('letter')).eq(index).contains(letter);
). Nous devrions d'ailleurs avoir cette préoccupation pour tous les types de tests et pour tout notre code en général, mais cela se traduit dans le cas spécifique des tests e2e par la création d'un Page Model, qui permet d'écrire des tests tels que celui de ce snippet. Avouez que c'est quand-même plus lisible, et même assez lisible dans l'absolu.
Concrètement, un Page Model n'est rien d'autre qu'un ensemble de fonctions ou de méthodes destiné à cacher l'implémentation des tests et faire abstraction du framework de tests. Sa structuration demande du travail, surtout si l'on veut pouvoir chaîner les actions et les assertions (
Player2.Guess(N._2nd).Letter(N._4th).click({ times: 2 }).expectStatusToBe('VALIDATED');
). Du travail, donc, mais rien de foufou : on n'envoie pas des gens sur la Lune.

Des tests coûteux à écrire et à exécuter

Page Model ou pas, ces tests sont particulièrement coûteux à écrire (couramment plusieurs heures pour le 1er test si l'on veut un vrai scénario métier), mais aussi particulièrement longs à exécuter (de l'ordre de la seconde, vs. 10 à 100ms pour des tests unitaires). En effet, on fait de vrais appels réseau et l'on utilise idéalement une vraie base de données.
Vous ne devez donc écrire que quelques tests e2e, concentrés sur les happy paths et les chemins critiques de l'application. Vous pouvez considérer les tests e2e comme des smoke tests, en application du proverbe selon lequel "il n'y a pas de fumée sans feu" : s'il y a de la fumée, c'est qu'il y a le feu quelque part dans l'application (condition nécessaire) ; mais pas de fumée n'implique pas qu'il n'y a pas de feu (condition non suffisante).
Par conséquent, votre stratégie de tests ne saurait reposer sur les seuls tests e2e, comme c'est malheureusement le cas bien souvent. Utilisez intelligemment toute la hauteur de la pyramide de tests, en commençant par des tests unitaires. Vraiment, comme pour tout, attention à la loi de l'instrument .
Il faut dire, les vendeurs de frameworks de tests, voulant favoriser l'adoption de leur produit, ont la fâcheuse tendance de communiquer sur le fait qu'il fait "Papa, Maman et le café". Cypress n'y échappe pas, qui assure être le meilleur pour faire des tests de composants. Mon avis ? Ces tests gagneraient à être faits en mockant le DOM, à l'aide de bibliothèques de test telles que Testing Library .