Archives par mot-clé : IoC

.NET : Améliorez vos tests unitaires avec Moq et NFluent

Une des bonnes pratiques d’un projet informatique est la mise en place de tests unitaires. Je vais vous présentez 2 librairies, disponible sous forme de package NuGet, pour vous aidez dans l’écriture de tests.

Moq

Le but d’un test unitaire est de tester un composant isolé, d’où la nécessité d’utiliser une librairie permettant de faire du Mock. J’utilise pour cela Moq.

Les exemples de tests suivant utilisent NUnit comme framework de tests unitaires. Afin de pouvoir facilement remplacer les dépendances des classes à tester, j’utilise Unity comme container d’injection de dépendance (IoC). Vous allez ainsi pouvoir facilement passer à votre classe dans le constructeur votre Mock.

Voyons comment configurer et effectuez des assertions sur vos mock avec Moq :

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using NFluent;
using NUnit.Framework;

namespace Demo.Tests
{
    [TestFixture]
    public class MyServiceTests : BaseServicesTests
    {
        private Mock<IMyService> myServiceMock;
        private Mock<ILogger> loggerMock;
        
        private IMyClass myClass;

        [SetUp]
        public override void SetUp()
        {
            base.SetUp();

            //Create Mocks
            myServiceMock = new Mock<IMyService>();
            loggerMock = new Mock<ILogger>();
                                  
           //We pass our mock to the instance to test
           myClass = new MyClass(myServiceMock, loggerMock);
        }

        [Test]
        public void MyMethodShouldReturnTrue()
        {
            //Setup method on service to return expected value to test our case
            //Here we setup to return an empty list of string
            myServiceMock.Setup(m => m.GetData()).Returns(new List<string>());
            
            var result = myClass.MyMethod();
            //Verify the result is what is expected
            Check.That(result).IsTrue();
            //Verify that our mock has been called
            myServiceMockSetup.Verify(m => m.GetData(), Times.Once);
        }
    }
}

Moq fournit un ensemble de méthodes permettant de configurer (Setup + Returns/Callbacks) et d’effectuer des tests sur les mocks (Verify).
On peut par exemple vérifier le nombre d’appel d’une méthode, les paramètres, …

Voici comment vérifier qu’un méthode prenant un paramètre de type string a bien été appelé 2 fois avec comme valeur de paramètre « MyTestString »

myServiceMock.Verify(r => r.MyMethod(It.Is<string>(x => x == "MyTestString")), Times.Exactly(2));

Le guide de démarrage de Moq

NFluent

Comme vous avez pu peut être le remarquer, je fais pas mal de JavaScript en ce moment et j’aime beaucoup les librairies de tests style jasmine, should, … qui permettent de décrire les assertions de manière plus lisibles. En .Net, j’utilise NFluent qui permet d’avoir une API d’assertion fluent où l’on peut chaîner les assertions à la suite les une des autres.

Check.That(julien)
    .IsNotNull()
    .And.IsNotEqualTo(david)
    .And.IsInstanceOf<Person>();

Dans l’exemple ci-dessus, je vérifie que le l’objet julien n’est pas null, n’est pas égal à david et que cet objet est une instance de la classe Person.

D’autres exemples d’assertions :

//Collections
//Nombre d'élément d'une collection
Check.That(persons).HasSize(3); 
//Il existe une personne ayant la propriété Name égale à Julien
Check.That(persons.Extracting("Name")).Contains("Julien"); 

//Test des exceptions
Check.ThatCode(() => myService.MyMethod())
    .Throws<MyException>()
    .WithMessage("MyMessage")
    .And.WithProperty("MessageKey", "MyMessageKey");

Voici des exemples d’assertion avec NFluent.

Liens

Bon tests 🙂 …

.NET / Unity : Récupérer un type parmi plusieurs implémentations

Une application est souvent séparée en plusieurs couches, chacune ayant une responsabilité bien définie :

  • Présentation
  • Accès aux données
  • Gestion des règles métiers

Afin d’avoir une application maintenable et testable il est nécessaire d’avoir un faible couplage entre les différentes briques de votre application.
Ce découplage passe par des interfaces qui vont définir le rôle de chaque brique et ce quelles sont capable de faire. Ainsi une brique ne sera pas dépendante d’une implémentation mais d’une interface. Par exemple, la couche présentation est liée via des interfaces à des services. Elle ne sait pas qui implémente ses services. La couche de présentation n’a pas de référence à une implémentation particulière des interfaces des services. On pourra ainsi facilement bouchonner (mock : renvoi de données de test) afin de tester plus facilement son application.

C’est la qu’intervient Unity.

Présentation de Unity

Unity est un conteneur d’injection de dépendances. Il fait partie des patterns & pratices de Microsoft. Unity va vous permettre de définir dans votre application qu’elle est la classe qui implémente une interface et va vous retourner pour vous une instance de cette classe.

La configuration de Unity peut se faire de 2 façons :

  • par code (via le méthode RegisterType)
  • par fichier xml

Je préconise de passer par un fichier XML qui lui permet de ne pas recompiler l’application.

Voici un exemple de configuration qui permet de définir qu’il faut utiliser la classe MonService lorsque l’on a besoin d’une interface IMonService.

<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
    <containers>
      <container>
        <types>
          <!-- Business services -->
          <type type="MyInterfaces.IMonService,MyInterfaces" mapTo="MyServices.MonService, MyServices"></type>
        </types>
      </container>
    </containers>
</unity>

La résolution par code se fait via la méthode Resolve sur le conteneur :

myContainer.Resolve<MyInterfaces.IMonService>();

Implémentation multiples d’une interface

Dans certains cas, il est possible que vous ayez besoin de plusieurs implémentations d’une même interface. (exemple : communication avec des machines ayant un protocole différents. On va ainsi utiliser le design pattern Commande qui va nous permettre d’avoir une seule interface de communication. Les implémentations permettent de masquer les différences de protocoles).
Il est possible de différentier les implémentations d’une même interface en spécifiant lors de la configuration de Unity un nom via l’attribut name.

Configuration par xml

<type type="MyInterfaces.IMonService, MyInterfaces" mapTo="MyServices.MonService1, MyServices" name="MonInterface1"></type>
<type type="MyInterfaces.IMonService, MyInterfaces" mapTo="MyServices.MonService2, MyServices" name="MonInterface2"></type>
<type type="MyInterfaces.IMonService, MyInterfaces" mapTo="MyServices.MonService, MyServices" name="MonInterface3"></type>

Configuration par code

myContainer.RegisterType<MyInterfaces.IMonService, MyServices.MonService1>("MonInterface1" ) ;

La résolution se fera via le code suivant :

myContainer.Resolve<MyInterfaces.IMonService>("MonInterface1");

Liens