Vous récupérez le code source d’une application et vous devez “refactorer” une partie du code ? Mais, aucun test unitaire ou métier n’a été effectué ? Dans ce cas, comment allez-vous tester vos modifications ? Est-ce qu’elles ont impacté le traitement de l’application ? Vous recherchez des renseignements sur Internet et vous découvrez le Golden Master Testing. Focus sur ce procédé de tests de non régression !
Le Golden Master Testing permet de sécuriser la refonte d’une application en appliquant une couche de test sur l’application initiale avant toute modification.
Ce que cette couche de tests stocke c’est le résultat des méthodes que l’on souhaite modifier avant la refonte. Après modification du code, il est pertinent de comparer le résultat de la méthode de “refactoring” avec le résultat stocké auparavant.
Ces tests permettent d’éviter la modification du traitement initial de la méthode.
Processus de mise en place
Pour chaque méthode que vous allez modifier, voici les étapes à respecter :
- Créez des méthodes de test qui appellent la fonction que vous souhaitez modifier avec plusieurs paramètres (pour tester le plus grand nombre de cas possibles),
- Stockez chaque résultat d’appel de vos méthodes de test dans des fichiers avec leurs paramètres,
- Créez une nouvelle méthode de test qui récupère chacun des fichiers créés ultérieurement et exécutez la fonction initialement réalisée lors du premier test (avec les paramètres contenus dans le fichier),
- Comparez et vérifiez que le résultat de ce test est identique à ceux des fichiers,
- Modifiez le code source de la méthode une fois avoir obtenu des résultats identiques,
- Relancez la méthode de test écrite à l’étape 3,
- Si le résultat de votre appel est toujours identique au résultat contenu dans votre fichier, le traitement de la méthode n’a pas été modifié. Dans ce cas, modifiez votre méthode jusqu’à passage du test au vert.
A noter
Le premier test qui permet de créer les fichiers de résultat est exécuté une seule fois avant le “refactoring”, il s’agit des valeurs de vérifications.
Si vous exécutez une nouvelle fois ces méthodes, vous allez devoir à nouveau créer des fichiers qui contiennent peut-être des valeurs différentes de celles initiales.
Une utilisation concrète du Golden Master Testing
Outils
Nous utiliserons :
- Un projet console sous Visual Studio 2017 qui contiendra la fonction à refactorer.
- Un projet de test qui contiendra tout le procédé de test (tests initiaux + tests de vérification).
Le résultat des tests initiaux sera stocké dans des fichiers texte.
Projet Console
Sous Visual Studio, commencez par créer un projet de type Console.
Dans la classe Program.cs, ajoutez une fonction static nommée DivideNumberPositive. C’est la méthode sur laquelle nous nous baserons pour le “refactoring”.
Elle prend en paramètre deux nombres et effectue le traitement suivant :
- Déclaration d’une variable nommée result qui contiendra le résultat retourné de la fonction.
- Vérification que les deux nombres passés en paramètre sont :
- Supérieur ou égal à 0. Sinon affectez la valeur -1 à result.
- Que le diviseur est différent de 0. Sinon affectez la valeur 0 à result.
- Sinon, effectuez la division et affectez le résultat à la variable result.
public static float DivideNumberPositive(float nbr, float divisor)
{
float result;
if(nbr < 0 || divisor < 0)
{
result = -1;
}
else if (divisor == 0)
{
result = 0;
}
else
{
result = nbr / divisor;
}
return result;
}
Testez votre fonction :
static void Main(string[] args)
{
float first_div = DivideNumberPositive(10, 2);
float second_div = DivideNumberPositive(-10, 2);
float third_div = DivideNumberPositive(10, 0);
Console.WriteLine(first_div + ";" + second_div + ";" + third_div);
Console.ReadLine();
}
La méthode DivideNumerPositive est appelée 3 fois, elle affiche le résultat de chaque appel dans la console. Les valeurs affichées en sortie sont les suivantes : 5 ;-1 ;0
Création du projet de tests
Dans la solution, ajoutez un projet de type Unit Test Project (.NET Framework) et renommez la classe par défaut en ProgramMasterTest.
Classe ProgramMasterTest
Cette classe va permettre de créer des fichiers textes de vérifications.
Elle appelle la méthode DivideNumberPositive avec différents paramètres puis crée un fichier texte contenant le résultat de chaque appel.
Créez ensuite trois cas de tests :
- Diviseur égal à 0. La méthode retournera 0.
- Diviseur inférieur à 0. La méthode retournera -1.
- Deux nombres positifs (diviseur + nombre). La méthode retournera le résultat de la division de ces deux nombres.
Chaque test appelle une méthode privée nommée DivideNumberPositiveCreateGoldenMasterTest.
Cette méthode prend en paramètre les arguments déclarés dans nos cas de tests. Elle appelle aussi la méthode DivideNumberPositive contenue dans notre projet console Application (n’oubliez pas d’inclure la référence).
Le résultat sera stocké dans une variable, qui sera converti en objet Json avant de créer un fichier texte avec le résultat.
Pour vérifier le résultat de notre appel, il sera nécessaire de comparer la valeur contenue dans le fichier avec l’objet Json.
Information : le nom des fichiers textes contient le nom de la méthode appelée et les paramètres d’appel de celle-ci. Ce qui permettra d’appeler la méthode de vérification avec les bons paramètres.
[TestClass]
public class ProgramMasterTest
{
[TestMethod]
public void DivideNumberPositive_DivisorEquals0()
{
float nbr = 10;
float divisor = 0;
DivideNumberPositiveCreateGoldenMasterTest(nbr, divisor);
}
[TestMethod]
public void DivideNumberPositive_DivisorLessThan0()
{
float nbr = 10;
float divisor = -5;
DivideNumberPositiveCreateGoldenMasterTest(nbr, divisor);
}
[TestMethod]
public void DivideNumberPositive_NbrEquals10_DivisorEquals2()
{
float nbr = 10;
float divisor = 2;
DivideNumberPositiveCreateGoldenMasterTest(nbr, divisor);
}
private void DivideNumberPositiveCreateGoldenMasterTest(float nbr, float divisor)
{
var model = Application.Program.DivideNumberPositive(nbr, divisor);
string expected = JsonConvert.SerializeObject(model);
string fileName = "DivideNumberPositiveCreateGoldenMasterTest_" + nbr + "_" + divisor + ".txt";
File.WriteAllText(fileName, expected);
string actual = File.ReadAllText(fileName);
Assert.AreEqual(expected, actual);
}
}
Les fichiers de résultats sont stockés dans le répertoire bin de notre application. Pour plus de lisibilité, Il est préférable de créer un dossier ou un zip par méthode à “refactorer”.
Une fois ces tests lancés et passés au vert, nous pouvons créer la classe ProgramTest, utile pour comparer nos fichiers de résultat avec l’appel de notre méthode DivideNumberPositive.
Classe ProgramTest
Maintenant, ajoutez une nouvelle classe de type Basic Unit Test au projet Application.Test.
Cette classe contient une méthode de test pour chaque méthode que l’on souhaite “refactorer” (dans notre cas, elle contiendra qu’une méthode pour DivideNumberPositive).
La méthode de test récupère tous les fichiers textes contenus dans notre dossier bin qui “match” avec notre expression régulière.
Pour chaque fichier, appelez la méthode DivideNumberPositive avec les attributs spécifiés dans le nom du fichier et convertissez ensuite le résultat en objet Json. Cet objet est ensuite comparé avec le résultat contenu dans le fichier.
[TestClass]
public class ProgramTest
{
[TestMethod]
public void DivideNumberPositiveCompareGoldenMasterTest()
{
string directoryName = AppDomain.CurrentDomain.BaseDirectory;
Regex regex = new Regex("(.*)DivideNumberPositiveCreateGoldenMasterTest_(.*)_(.*).txt");
foreach (var fileName in Directory.GetFiles(directoryName, "*.txt"))
{
var match = regex.Match(fileName);
if (!match.Success)
continue;
int index = 2;
float nbr = match.GetFloat(index);
float divisor = match.GetFloat(++index);
string expected = File.ReadAllText(fileName);
var model = Application.Program.DivideNumberPositive(nbr, divisor);
string actual = JsonConvert.SerializeObject(model);
Assert.AreEqual(expected, actual);
}
}
}
Information : pour récupérer les paramètres contenus dans le nom du fichier, surchargez la classe Match avec la méthode nommée GetFloat. Cette méthode va convertir les arguments récupérés dans le nom du fichier (type string) en float.
public static class Helpers
{
public static float GetFloat(this Match match, int index)
{
return float.Parse(match.Groups[index].Value);
}
}
Lancez ensuite l’exécution de votre méthode et vérifiez que le cas de test passe au vert. S’ils le sont, commencez à modifier le code source de votre méthode.
Refactoring de la méthode DivideNumberPositive
Au lieu d’avoir une variable result que nous nous apprêtons à “setter” et que nous retournons à la fin de notre fonction, retournez directement votre valeur (oui, c’est un gros changement en perspective je vous l’accorde…).
public static float DivideNumberPositive(float nbr, float divisor)
{
if (nbr < 0 || divisor < 0)
{
return -1;
}
else if (divisor == 0)
{
return 0;
}
else
{
return (nbr / divisor);
}
}
Relancez ensuite la méthode de vérification.
Le test est toujours vert. Nos modifications n’ont pas impacté le traitement de la méthode.
Modifiez la méthode, lorsqu’un des paramètres est négatif, nous renverrons la valeur 10.
public static float DivideNumberPositive(float nbr, float divisor)
{
if (nbr < 0 || divisor < 0)
{
return 10;
}
else if (divisor == 0)
{
return 0;
}
else
{
return (nbr / divisor);
}
}
Relancez la méthode de vérification.
Le test a échoué.
Le résultat retourné par notre méthode est 10, alors que le résultat contenu dans notre fichier est -1. Nos deux valeurs ne sont pas égales. Notre changement a donc impacté le traitement de la méthode.
Remettez la valeur à -1 et relancez le test.
Le test passe au vert !
Pour conclure, Golden Master Testing est un processus de test permettant de vérifier que chaque modification du code source n’a pas impacté le traitement de l’application. Dans notre cas, nous l’avons utilisé dans une mission de “refactoring” contenant peu de tests métiers et unitaires. En n’ayant aucune idée significative de l’action et du résultat retourné par la méthode, vous pouvez cependant sécuriser vos refontes avec ces tests et valider qu’aucun changement n’a impacté le cœur métier de l’application auprès du client.