Prenons le code métier suivant

 
public class TestedClass {
public String myMethod(String str) {
return str.concat(this.getExtension());
}

private String getExtension() {
// in real life, we could imagine this value is retrieved from a
// properties file or ...
return ".extension";
}
}

Prenons le code de test suivant:

public void testMyMethod() {
TestedClass obj = new TestedClass();
String param = "test";
String actualValue = obj.myMethod(param);
assertNotSame(param, actualValue);
}

Le test semble correct: il exécute une méthode et vérifie un résultat obtenu. La couverture de test sur la méthode myMethod() sera de 100% et permettra d'être satisfait. Toujours est il que dans ce cas, le rôle premier du test - qui est d'assurer la non-régression du code métier - n'est pas rempli. Si le code métier change, le code de test n'échouera pas. Modifions par exemple la méthode qui retourne l'extension:

 
private String getExtension() {
// in real life, we could imagine this value is retrieved from a
// properties file or ...
return ".otherextension";
}

Rejouez le test, il passe, la couverture de test reste à 100% et pourtant une régression a été introduite dans le code métier. Simplement, il manque une assertion assertEquals("test.extension", actualValue);

Dès lors, on se rend compte qu'il manque un indicateur relatif à la pertinence du test: Dans quelle mesure mon test me garantit-il la détection de régressions?

Nos expériences nous donnent deux pistes:

  • Compter le nombre d'assertions (à l'aide d'un grep par exemple). Il semblerait que 5 % d'assertions soit le minimum. "5% d'assertions", cela signifie que 5 assertions utiles (donc autres que assertTrue(true)...) sont réalisées pour 100 lignes de code métier exécutées. Mais cet indicateur a aussi sa limite et notre exemple simpliste le montre. En effet, 2 lignes de code métier sont éxécutées et une assertion "contextuellement mauvaise" de type assertNotSame est réalisée. Nous sommes donc à 50% d'assertions et pour autant, la regression introduite dans le code n'a pas été détecté par le test...
  • Travailler sur des outils qui introduisent de manière aléatoire des regressions dans le code métier et vérifient si le test échoue ou non. On s'y essaye actuellement et les idées principales reposent:
    • Sur l'utilisation d'AOP pour modifier de manière aléatoire le comportement des méthodes du code métier. A titre d'exemple, on modifie des paramètres ou des résultats de méthodes (inversion du premier et dernier caractère d'un objet de type java.lang.String, multiplication par deux d'un int, lancement d'une exception non-typée en lieu et place du comportement classique de la méthode...)
    • Sur l'utilisation de Maven pour exécuter plusieurs fois les tests "corrompus" ainsi que pour mettre à disposition un rapport html reprenant l'ensemble des modifications effectuées et essayant de donner un indicateur sur la pertinence du harnais de tests

De manière simple, plus la pertinence du test est bonne, plus une modification du code métier sera détectée rapidement. Le test réalisé uniquement avec assertNotSame(param, actualValue); aurait une pertinence quasi null - car aucune modification du code métier ne le ferait échouer - alors que le même test avec l'assertion supplémentaire assertEquals("test.extension", actualValue); deviendrait pertinent.

Ceci étant, je ne vous mentirai pas: il reste du travail :o)