Dans les bonnes pratiques de tests, on conseille souvent de tester le comportement et non les détails d’implémentation. En apparence, il est facile de faire la distinction, mais en pratique cela est souvent plus compliqué.

Nous allons donc essayer d’ajouter quelques précisions et des exemples afin de pouvoir mieux identifier l’un et l’autre.

Quel différence d’intention y a-t-il ?

Tester un comportement

L’intention est de tester la couche métier du programme :

Dans les conditions X, je teste que ma fonction/module/système me renvoie le résultat Y. Je ne souhaite pas et ne devrait pas savoir comment est implémenté la solution.

Ex: Dans une voiture, sur un banc de test, je teste que lorsque j’appuie sur la pédale de frein, les roues d’arrêtent de tourner.

⚠️ Test de comportement ≠ Test fonctionnel/intégration. Notre exemple n’est pas un test fonctionnel, car il ne permet pas seul de s’assurer que la voiture s’arrête correctement (la voiture peut avoir de mauvais pneus) : il valide simplement le comportement correct de la pédale de frein.

Tester une implémentation

L’intention est de tester la couche infrastructure du programme

Sans savoir dans quel contexte métier je me situe, lorsque j’appelle la fonction X, je vérifie que mon système est bien dans l’état Y

Ex: Dans une voiture, un fil électrique est abimé et je sais pas à quoi il correspond, je le remplace et vérifie que le courant passe bien avec un testeur de continuité.

⚠️ On comprend bien ici les limites de tester l’implémentation : on a aucune idée si la voiture est bien réparée ou non…

Comment ça se traduit dans du code ?

Exemple de test de comportement

// Un service représentant le métier d'un client : un service de blog
const customerBlogService = {
  canPublish(user): boolean {},
  allowPublish(user): void {},
  publish(user, article): void {}
};

test('allowPublish() should grant publish access to user', () => {
	const anyUser = generateUser();
  expect(customerBlogService.canPublish(anyUser)).toBe(false);

  blogService.allowPublish(anyUser);
  expect(customerBlogService.allowPublish(anyUser)).toBe(true);

  // On peut facilement comprendre ce code d'un point de vue métier
  // - un utilisateur ne peut pas publier par défaut
  // - on peut savoir si un utilisateur est autorisé à publier avec canPublish()
  // - on peut autoriser un utilisateur à publier en utilisant allowPublish()
});

customerBlogService ne manipule que des concepts “métier” (ex: blog, publish)

✓ On comprend le métier du client en lisant les tests

✓ Si on change l’implémentation de customerBlogService (remplacer une database par un service externe par exemple), ces tests n’auraient pas besoin d’être modifiés