DEV Community

Cover image for Deux Exemples Fréquents de Couplage Tests/Implémentation
Fouad
Fouad

Posted on • Originally published at fouadhamdi.com

Deux Exemples Fréquents de Couplage Tests/Implémentation

Supposons que vous souhaitez tester la classe User suivante (très simpliste pour l'exemple):

public class User {
    private String firstname;
    private String lastname;

    public User(String firstname, String lastname) {
        this.firstname = firstname;
        this.lastname = lastname;
    }

    public String displayName() {
        return firstname + " " + lastname;
    }
}
Enter fullscreen mode Exit fullscreen mode

Voici comment certains développeurs feraient:

@Test
void displayName() {
    var firstname = "John";
    var lastname = "Doe";
    var user = new User(firstname, lastname);

    assertEquals(firstname + " " + lastname, user.displayName(), "The display name should contain the firstname and the lastname.");
}
Enter fullscreen mode Exit fullscreen mode

plutôt que:

@Test
void displayName() {
    var user = new User("John", "Doe");

    assertEquals("John Doe", user.displayName(), "The display name should contain the firstname and the lastname.");
}
Enter fullscreen mode Exit fullscreen mode

La logique de leur raisonnement est de vouloir supprimer la duplication des constantes représentant le nom et le prénom dans le test. Parce que la duplication, c'est forcément BAD.

Au-delà du fait que le second test est plus court et plus simple à lire, éliminer cette duplication en introduit une autre qui est bien plus dangereuse.

Sauriez-vous dire laquelle ?

Il s'agit du code firstname + " " + lastname présent dans le premier test. Et ce que l'on a dupliqué ici est l'algorithme utilisé par la classe User pour générer le nom à afficher.

En faisant cela, on introduit un couplage entre notre test et son implémentation. Et c'est une chose à éviter.

Pour quelle raison ? Parce plus un test est indépendant de l'implémentation du code testé, meilleur il est: imaginez que l'algorithme change côté production, voilà qu'on doive également le transposer dans les tests.

Traitez le code de production comme une boîte noire pour éviter de construire des tests fragiles.

Bien sûr, ce n'est parfois pas possible (j'en parlerais peut-être une autre fois) mais on doit s'évertuer à suivre cette règle: cela rendra nos tests beaucoup moins fragiles.

En résumé, ne copiez pas l'algorithme à tester dans vos tests !

Il y a une autre forme de duplication plus subtile mais que j'ai souvent vue même chez des développeurs expérimentés. Considérez le test d'intégration suivant:

@Test
void findUser() {
    var expectedUser = new User(1, "John", "Doe");
    var response = restTemplate.getForEntity("/users/1", User.class);
    JSONAssert.assertEquals(expected, response.getBody(), false);
}
Enter fullscreen mode Exit fullscreen mode

J'ai éliminé toute la tuyauterie pour nous concentrer sur la partie utile du test: on utilise Spring Boot mais ça pourrait être autre chose.

Quel est le problème ici ?

Eh bien, on utilise la classe User dans notre test d'intégration et cette classe vient de notre code de production.

Pareil que pour précédemment, on a couplé notre test avec le code de production.

La justification est souvent de dire qu'il est plus simple de réutiliser la classe User plutôt que d'écrire notre test indépendamment de celle-ci.

Supposez que l'on renomme des propriétés dans la classe User dans le cadre d'un refactoring: ce test passera toujours et ne le détectera pas.

Cela veut dire que notre API ne respectera plus son contrat et ce sont les clients de cette API qui vont être mécontents.

Pour les tests d'intégration de ce type, n'utilisez pas de code côté production. Voici un meilleur test dans ce contexte:

@Test
void findUser() {
    String expectedUser = "{id:1, firstname:\"John\", lastname:\"Doe\"}";
    var response = restTemplate.getForEntity("/users/1", String.class);
    JSONAssert.assertEquals(expected, response.getBody(), false);
}
Enter fullscreen mode Exit fullscreen mode

La solution est simplement de considérer la sortie JSON comme une String plutôt qu'un User.

Certes, cela n'a pas l'air joli mais pareil qu'avant, votre test sera beaucoup plus robuste en capturant les modifications qui changent le contrat de vos APIs.

Top comments (0)