Quand on développe, arrive le moment fatidique des tests. On serait tenté de se dire ‘je suis développeur pas testeur… Chacun son job’. Malheureusement ça ne se passe pas toujours comme ça dans la vraie vie (dur dur d’être développeur :)). On doit parfois passer beaucoup de temps à essayer d’écrire les tests les plus exhaustifs afin d’avoir la couverture de code la plus élevée possible et éviter au maximum les surprises lors de l’exécution. Il faut tester à la fois que le code fait ce qu’il est censé faire, mais aussi qu’il ne plante pas lamentablement parce que l’on a oublié de tester un cas particulier. Ce travail est très fastidieux et force est de constater qu’on arrive difficilement à faire les choses comme il faut.

Heureusement les chercheurs de Microsoft essaient de nous rendre la vie un peu plus facile. En effet grâce à Pex, un outil développé par Microsoft Research, nous n’avons presque plus à nous préoccuper des tests (bon d’accord, j’exagère peut-être un peu ;)). Pour faire simple Pex analyse notre code dans sa version compilé, exécutable ou DLL (ce qui le rend compatible avec tous les langages dotnet), et essaie de créer des tests avec la couverture de code la plus élevée pour chaque méthode. Son but n’est pas de valider la logique (puisqu’il n’est pas encore capable de deviner ce que le développeur cherche à faire), mais plutôt de vérifier que tous les cas sont testés. Cela signifie que Pex cherche à identifier les arguments utiles permettant de passer par toutes les branches d’une méthode sans pour autant multiplier les tests passant par la même branche.

Pour arriver à ses fins, Pex ne génère pas des arguments aléatoires (Fuzzing) mais comprend le code et essaye de trouver les arguments corrects (grâce au moteur Z3)

Commençons par un exemple plutôt simple :

public static void AnswerToTheUltimateQuestionOfLifeTheUniverseAndEverything(int i)
{
   if (i > 41)
   return;
   throw new Exception("Try again...");
}

pex exploration results

Pex trouve la bonne réponse :). Voyons maintenant un exemple de ce que Pex propose avec un exemple mathématique légèrement plus compliqué (que de bons souvenirs ;))

public static void TestMath(object o, int[] values)
{
   // Preconditions
   if (values == null)
   throw new ArgumentNullException("values");
   if (values.Length < 3)
   throw new ArgumentException("values must have at least 3 elements", "values");
   
   if (values[0] == values[1] / 2)
   {
   o.ToString(); // NullReferenceException
   }
   else if (values[0] + values[1] == (values[1] * 71 + values[2] + 42) / 2)
   {
   throw new Exception("Simple equation");
   }
   
   // Pex permet de vérifier que 2 nombres sont premiers entre eux (Si si je vous assure) !!!
   // La preuve en utilisant le théorème de Bachet-Bézout (http://fr.wikipedia.org/wiki/Nombres_premiers_entre_eux)
   // La probabilité de choisir au hasard 2 nombres premiers entre eux est de 6/(Pi^2) => Forçons le destin avec 2 nombres consécutifs :)
   if (41 * values[0] + 42 * values[1] == GCD(41, 42)) // 41x+42y=1
   {
   throw new Exception("41 and 42 are coprimes");
   }
}
 
private static int GCD(int a, int b)
{
   return b == 0 ? a : GCD(b, a % b);
}

pex exploration results

Comme on peut le voir, les exceptions lancées sur les arguments (ArgumentNullException, ArgumentException, ArgumentOutOfRangeException, etc. ) sont considérées comme valides (vert) alors que les autres sont considérées comme des erreurs (rouge). On peut indiquer à Pex qu’une exception est attendue :

exception attendue

Et voilà :

résultat

En plus de savoir résoudre des équations, Pex comprend aussi les Regex

public static void TestString(string s)
{
   if (Regex.IsMatch(s, @"\w+\d{2}"))
   throw new Exception("Match");
}

inputs-outputs

Cependant il y a de nombreux cas que Pex ne peut pas résoudre

public void TestXml(XDocument doc)
{
   if (doc == null)
   throw new ArgumentNullException("doc");
   if (doc.Descendants("Tests").Any())
   throw new Exception(); // Pex can’t create the XDocument itself
}

Trouver des valeurs intéressantes pour les tests c’est bien, mais Pex permet aussi de sauvegarder les tests unitaires générés afin de les rejouer par la suite. Pour cela Pex génère plusieurs fichiers :

  • Un fichier par classe : {class name}Test. cs
  • Un fichier par méthode de la classe : {class name}Test. {method name}. g. cs

console application 1

Le premier fichier contient des stubs de tests du type :

[PexMethod, PexAllowedException(typeof(ArgumentException)), PexAllowedException(typeof(ArgumentNullException)), PexAllowedException(typeof(Exception))]
public void TestMath(object o, int[] values)
{
   Program.TestMath(o, values);
   // TODO: add assertions to method ProgramTest.TestMath(StringBuilder, Int32[])
}

Le deuxième fichier contient les tests avec les valeurs trouvées par Pex durant l’analyse :

[TestMethod]
[PexGeneratedBy(typeof(ProgramTest))]
[PexRaisedException(typeof(NullReferenceException))]
public void TestMathThrowsNullReferenceException543()
{
   int[] ints = new int[3];
   this.TestMath((StringBuilder)null, ints);
}
 
[TestMethod]
[PexGeneratedBy(typeof(ProgramTest))]
public void TestMath900()
{
   int[] ints = new int[3];
   ints[0] = 427;
   ints[1] = 8;
   ints[2] = 245;
   this.TestMath((StringBuilder)null, ints);
}

L’intérêt de créer des stubs est de pouvoir ajouter des tests logiques (ce que doit réellement faire la méthode testée), tests qui seront vérifiés pour chaque test généré par Pex.

Pour tous ceux qui se disent, j’utilise NUnit (pas Microsoft), donc Pex (Microsoft) ne va pas fonctionner avec mes projets NUnit, c’est faux. Pex peut très bien générer du code pour d’autres Framework de tests. Il supporte MSTest, xUnit.NetNUnitMbUnit.

Comme on peut le voir Pex s’intégre parfaitement à Visual Studio 2008 et 2010. Et oui, pas besoin de quitter son environnement de développement préféré. Il suffit de cliquer droit sur ce que l’on veut tester et Pex se lance. Que demander de plus ? (si ce n’est de supporter les versions plus récentes de VS 2012 et 2013)

Pour Visual Studio 2012, l’équipe a créé un plugin Microsoft Code Digger, cependant celui-ci permet uniquement de générer des arguments intéressants pour une méthode, pas de génération de tests. Toujours très simple d’utilisation : clic droit -> Generate Inputs / Outputs Table

Dernier point, Pex est utilisable en ligne de commande (voir la documentation) ce qui permet par exemple de l’intégrer aux nightly build.

Cela est une présentation sommaire de cet outil. Pour bien comprendre les possibilités, je vous conseille de consulter le document PDF de l’équipe en charge du développement de Pex : http://research.microsoft.com/en-us/projects/pex/pexmanual.pdf

PS : Pour ceux qui ne sont pas convaincus, essayer rapidement la version en ligne : http://www.pexforfun.com/

Ne ratez plus aucunes actualités avec la newsletter mensuelle de SoftFluent