Laurent Kempé
Rendre une classe testable par refactoring
Cet article vous présente une session de refactoring afin de rendre une portion de code plus testable de façon unitaire
Par Laurent Kempé publié le 11/01/2010 à 18:30, lu 2240 fois, 4 pages
 3 | Mise en pratique du Refactoring
La première étape de notre refactoring concerne la classe JobPostControllerValidation. Cette classe est statique car il s’agit d’une classe d’extension.
Regardons de plus prêt la première méthode.
A l’aide de Resharper nous allons utiliser l’affichage en couleur de l’utilisation (CTRL-SHIFT-F7) de JobPostController. En y regardant de plus prêt nous nous rendons compte que la seule propriété utilisée est ModelState.
 
/content/d79c6699-77a2-46a8-a13c-0e8313509221/image6.png
 
Nous allons donc introduire un nouveau paramètre (CTRL-ALT-P), ou en utilisant le menu contextuel de refactoring (CTRL-SHIFT-R)
 
/content/d79c6699-77a2-46a8-a13c-0e8313509221/image7.png
 
Et bien entendu remplacer les 5 occurrences
 
/content/d79c6699-77a2-46a8-a13c-0e8313509221/image8.png
 
Nous nommons ce nouveau paramètre modelState
 
/content/d79c6699-77a2-46a8-a13c-0e8313509221/image9.png
 
Nous vérifions enfin que notre méthode n’utilise plus le paramètre de type JobPostController, en positionnant le curseur sur le paramètre jc et CTRL-SHIFT-F7
 
/content/d79c6699-77a2-46a8-a13c-0e8313509221/image10.png
 
Puis nous démarrons un Safe Delete (ALT-DEL) du paramètre jc
 
/content/d79c6699-77a2-46a8-a13c-0e8313509221/image11.png
 
Nous effectuons la même opération sur la méthode ValidateJobPost afin d’obtenir le résultat suivant :

public static class JobPostControllerValidation

{

    public static bool ValidateApplyForm(JobPostApplyForm form, ModelStateDictionary modelState)

    {

public static bool ValidateJobPost(JobPostViewData jobPost, ModelStateDictionary modelState)

{

Maintenant que notre classe n’est plus une classe d’extension nous pouvons déclarer la classe comme non static. Puis nous créons une nouvelle méthode newValidateApplyForm qui n’est rien d’autre qu’un wrapper autour de la méthode statique ValidateApplyForm.

public class JobPostControllerValidation

{

    public bool newValidateApplyForm(JobPostApplyForm form, ModelStateDictionary modelState)

    {

        return ValidateApplyForm(form, modelState);   

    }

 

    public static bool ValidateApplyForm(JobPostApplyForm form, ModelStateDictionary modelState)

    {

Nous naviguons sur l’appel de la méthode ValidateApplyForm à l’aide de ReSharper (AlT-F7), qui ressemble à cela :

if (! JobPostControllerValidation.ValidateApplyForm(form, this.ModelState))

    return View(vd);

Que nous pouvons maintenant modifier en utilisant le fait que notre classe JobPostControllerValidation n’est plus static :

var jobPostControllerValidation = new JobPostControllerValidation();

 

if (!jobPostControllerValidation.newValidateApplyForm(form, this.ModelState))

    return View(vd);

Ceci ne résout pas notre souci de création de dépendance, nous devons donc créer une propriété (CTRL-ALT-D) _jobPostControllerValidation à notre classe JobPostController:
 
/content/d79c6699-77a2-46a8-a13c-0e8313509221/image12.png
 
 
/content/d79c6699-77a2-46a8-a13c-0e8313509221/image13.png
 
Enfin nous déplaçons la création de jobPostControllerValidation de façon temporaire dans le constructeur, nous y reviendrons plus tard :

public class JobPostController : BaseController

{

    private readonly JobPostControllerValidation _jobPostControllerValidation;

 

    public JobPostController(IJobSystem jobSystem)

    {

        _jobSystem = jobSystem;

        _jobPostControllerValidation = new JobPostControllerValidation();

 

    }

Au passage on en profite pour passer la propriété _jobPostControllerValidation en readonly à l’aide de Resharper (ALT-ENTER) :
 
/content/d79c6699-77a2-46a8-a13c-0e8313509221/image14.png
 
 
/content/d79c6699-77a2-46a8-a13c-0e8313509221/image15.png
 
Nous créons toujours la dépendance au lieu de la recevoir par injection à travers le constructeur, mais nous nous rapprochons de notre but.
Nous retournons dans la classe JobPostControllerValidation et nous allons maintenant éliminer la méthode static ValidateApplyForm à l’aide du refactoring Resharper ‘Inline Method’ (CTRL-SHIFT-R) :
 
/content/d79c6699-77a2-46a8-a13c-0e8313509221/image16.png
 
Et nous en profitons pour renommer notre méthode (F2) de newValidateApplyForm à ValidateApplyForm, son nom original :
 
/content/d79c6699-77a2-46a8-a13c-0e8313509221/image17.png
 
Nous obtenons donc le code suivant :

public class JobPostControllerValidation

{

    public bool ValidateApplyForm(JobPostApplyForm form, ModelStateDictionary modelState)

    {

        if (String.IsNullOrEmpty(form.Name))

            modelState.AddModelError("Name", "Please supply your name.");

 

        if (String.IsNullOrEmpty(form.EmailAddress))

            modelState.AddModelError("EmailAddress", "Please supply your email address.");

 

        var emailRegex = new Regex(@".*@.*\..{2,3}");

        //really crap regex -> replace and move to global reference file!

        if (!emailRegex.IsMatch(form.EmailAddress))

            modelState.AddModelError("EmailAddress", "The email address supplied is not valid");

 

        if (String.IsNullOrEmpty(form.IntroductionLetter))

            modelState.AddModelError("IntroductionLetter", "Please supply your an introduction letter.");

 

        return modelState.IsValid;

    }

 

    public static bool ValidateJobPost(JobPostViewData jobPost, ModelStateDictionary modelState)

    {

Nous répétons l’opération pour la méthode ValidateJobPost. Nous avons donc maintenant une classe JobPostControllerValidation qui n’a plus aucune méthode static.
Nous pouvons donc extraire une interface (CTRL-SHIFT-R) à partir de cette classe
 
/content/d79c6699-77a2-46a8-a13c-0e8313509221/image18.png
 
 
/content/d79c6699-77a2-46a8-a13c-0e8313509221/image19.png
 
Ce qui donne la chose suivante :

public class JobPostControllerValidation : IJobPostControllerValidation

{

    public bool ValidateApplyForm(JobPostApplyForm form, ModelStateDictionary modelState)

    {

public interface IJobPostControllerValidation

{

    bool ValidateApplyForm(JobPostApplyForm form, ModelStateDictionary modelState);

    bool ValidateJobPost(JobPostViewData jobPost, ModelStateDictionary modelState);

}

Nous pouvons donc maintenant utiliser cette interface dans la classe JobPostController, CTRL-SHIFT-R :
 
/content/d79c6699-77a2-46a8-a13c-0e8313509221/image20.png
 
 
/content/d79c6699-77a2-46a8-a13c-0e8313509221/image21.png
 
Et finalement au lieu de créer la dépendance nous l’ajoutons au constructeur pour passer de

public class JobPostController : BaseController

{

    private readonly IJobPostControllerValidation _jobPostControllerValidation;

 

    public JobPostController(IJobSystem jobSystem)

    {

        _jobSystem = jobSystem;

        _jobPostControllerValidation = new JobPostControllerValidation();

    }

à

public class JobPostController : BaseController

{

    private readonly IJobPostControllerValidation _jobPostControllerValidation;

 

    public JobPostController(IJobSystem jobSystem, IJobPostControllerValidation jobPostControllerValidation)

    {

        _jobSystem = jobSystem;

        _jobPostControllerValidation = jobPostControllerValidation;

    }

Voilà ! La classe JobPostController est enfin testable de façon unitaire car elle n’a plus que des dépendances avec des interfaces qui lui sont fournies par son constructeur.
 
» Démarrer une discussion