Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

Fathi Bellahcene

.Net m'a tuer!
[VS 2012-Fakes Framework] Bienvenue chez les Shims!

 

Lorsque l’on fait des Tests unitaires, l’une des principales difficultés que l’on rencontre, c’est l’isolation de la méthode que l’on souhaite tester : très souvent ca passe par des mocks, des stubs ou des fakes.

Là où les choses se compliquent, c’est lorsque vous utiliser des API ou des Framework externes dont vous ne maitriser pas le code et là, bonjour la galère pour mocker votre système…et dans bien des cas, ça devient tellement complexe (et/ou couteux) que l’on préfère ne pas tester ces portions de code.

On va voir dans ce post comment le « Fakes Framework » inclus dans VS 2012 permet de manière élégante et très simple de solutionner ce problème.

Prenons cet exemple :

Nous avons un Framework avec une classe ParamManager (sealed…comme ca on ne peut pas générer de stub via RhinoMock ;) ) et qui possède une méthode statique GetValue.

 
public sealed class ParamManager
    {
        public static int GetValue(string paramName)
        {
            if (string.Equals(paramName, "A",
 StringComparison.InvariantCultureIgnoreCase))
            {
                return 5;
            }
            else
            {
                return 2;
            }
        }
    }

Puis nous avons le code que nous développons : la classe Foo qui utilise la classe ParamManager via la classe GetFoo:

    public class Foo
    {
        public int GetFoo(int initalValue)
        {
            var paramName = "a";
            int paramValue = ParamManager.GetValue(paramName);
 
            return paramValue * initalValue;
        }
    }

Avant le « Fakes framework », on aurait fait de l’IoC afin de casser le couplage entre les classes Foo et ParamManager et permettre de facilement isoler et tester la classe Foo :

On casse la dépendance en utilisant une interface IParamManager qui est utilisée par notre classe FooClean.

    public class FooClean
    {
        private IParamManager _paramManager;
 
        public FooClean(IParamManager paramManager)
        {
            _paramManager = paramManager;
        }
 
 
        public int GetFoo(int initalValue)
        {
            var paramName = "a";
            int paramValue = _paramManager.GetValue(paramName);
 
            return paramValue * initalValue;
        }
    }
 
    public interface IParamManager
    {
        int GetValue(string paramName);
    }

On ajoute une nouvelle classe MyParamManager qui implémente l’interface IParamManager et qui utilise la classe du framework ParamManager.

public class MyParamManager : IParamManager
    {
 
        public int GetValue(string paramName)
        {
            return ParamManager.GetValue(paramName);
        }
    }

Lorsque l’on est dans une configuration « métier » (ie on souhaite utiliser la classe FooClean à l’identique de la classe Foo); on utilisera donc la classe FooClean avec l’objet MyParamManager en paramètre du constructeur (on fait de l’injection par constructeur).

Du coup, tester notre nouveau code devient simple en utilisant un framework de Mock comme RhinoMock par exemple:

       [TestMethod]
        public void TestFooClean()
        {
            var p = MockRepository.
                    GenerateMock<IParamManager>(null);
            p.Stub(x => x.GetValue("a")).Return(4);
            
            FooClean f = new FooClean(p);
            var value = f.GetFoo(2);
 
            Assert.AreEqual(8, value);
        }

Ça marche nickel et je dirais même que c’est une bonne solution car elle permet aussi de découpler notre code…sauf que dans ce cas simple, ça marche comme sur des roulettes mais dans des cas compliqués ou les objets de notre framework interagissent entre eux : ça devient quasi impossible d’avoir un code proprement mockable donc testable unitairment.

VS 2012 permet de résoudre simplement et de manière élégante cette problématique. Reprenons notre exemple et voyons comment tester notre classe Foo sans avoir à inverser les dépendances.

On va utiliser les Shims :les shims permettent d’intercepter et de rediriger au runtime des appels de méthodes. Ainsi, lorsque notre objet de type Foo va faire son appel à la methode GetValue, on va pouvoir rediriger cet appel vers une autre méthode que l’on aura défini pour les besoin de notre test. Du coup, plus besoin d’IoC (de modification du code) ou de mocks, on peut tester la classe Foo sans avoir à la modifier!

Comment on fait ? Premièrement, on doit ajouter une dll « Fake » pour la dll « Framework » : c’est dans cette nouvelle dll que vont être générer automatiquement les shims et à partir desquelles on va pouvoir créer nos méthodes de substitution.

Pour ca, click droit sur la réference et “Add Fakes Assembly”: 

image

Ensuite, on va utiliser l’objet généré pour définir notre méthode de substitution, une simple lambda qui renvoi tjr –4:

 

ShimParamManager.GetValueString = s => -4;

Enfin, on va pouvoir écrire notre test sur la méthode GetFoo de la classe Foo :

[TestMethod]
        public void TestFoo()
        {
            using (ShimsContext.Create())
            {
                ShimParamManager.GetValueString = s => -4;
 
                Foo f = new Foo();
                var value = f.GetFoo(2);
                Assert.AreEqual(-8, value);
            }
        }

Ca marche et sans avoir toucher une ligne de notre code initiale Sourire

Vous devez ajouter un block using avec un “ShimContext” tout simplement pour s’assurer qu’a la fin du block, les shims définis soient tous disposés. Comme ca, vous ne risquer pas d’avoir de télescopage entre deux TU.

Il aussi est important de souligner que le « Fake Framework » sera (malheureusement) disponible avec la version Ultimate de VS 2012 et que les classes de base du framework .NET « mscorlib » et « system» ne peuvent avoir de Shims.

Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :
Posted: dimanche 1 juillet 2012 00:27 par fathi

Commentaires

Pas de commentaires

Les commentaires anonymes sont désactivés

Les 10 derniers blogs postés

- Intégration Yammer et SharePoint Online (Office 365), étape 1 … par Le blog de Patrick [MVP SharePoint] le 06-12-2013, 17:37

- [Dynamics CRM] Ajouter les dossiers de CRM au dossier Favoris d’Outlook par Christine Dubois le 06-10-2013, 15:50

- Visual Studio 2013 par Etienne Margraff le 06-04-2013, 10:26

- Configurer la collation SQL Server pour SharePoint par Blog de Jérémy Jeanson le 06-03-2013, 19:48

- Etendre le Team Web Access de TFS 2012 – Step 1: Création du plugin par Philippe Didiergeorges Aka Philess le 06-03-2013, 07:30

- Livre Blanc : Développer des applications NUI par Fathi Bellahcene le 06-01-2013, 11:35

- [Dynamics CRM 2011] Copier une vue d'entité par Christine Dubois le 05-29-2013, 13:20

- [Conf’SharePoint 2013] Mes présentations… par Le blog de Patrick [MVP SharePoint] le 05-28-2013, 09:04

- [wpdev] Storage bug in MediaLibrary.SavePicture par Kévin Gosse le 05-26-2013, 19:08

- VMMap en mode instrumentation sur système 64bit : attention à la plateforme cible du build .NET par CoqBlog le 05-25-2013, 22:25