Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

Frédéric Hamel

Assert.Success();

[Unit Test] Tests unitaires et développement par le test facilités avec Rhino Mock 3.5

Introduction

Rhino Mock 3.5 a été amélioré pour tirer parti au maximum de la nouvelle syntaxe offerte par C# 3 et .NET 3.5. On peut maintenant utiliser des expressions lambda et la syntaxe est vraiment très agréable et efficace.

Je pense qu'à cette occasion, il est important d'écrire un post pour militer en faveur des tests unitaires et du développement par le test. Souvent, j'entends dire que le développement par le test est bien, mais qu'en pratique il n'est pas facile à mettre en place. En fait, c'est surtout une question d'habitude puisque c'est une façon de programmer différente ; de bons outils et réflexes d'architecture sont par ailleurs nécessaires pour se simplifier la tâche.

Rappels sur les Mocks et leur utilité

Pour ceux qui ne savent pas encore ce qu'est un "Mock", je vous recommande vivement l'article de Martin Fowler Mocks Aren't Stubs.

Pour résumer, un mock est un objet qui imite un autre objet à des fins de test. Cela permet de développer, par exemple, le contrôleur et le modèle d'un pattern MVC avant d'avoir la vue, car on va "Mocker" (singer) la vue et tester spécifiquement le contrôleur sans avoir besoin de lancer l'application à chaque fois. Dans l'outil financier que mon entreprise développe actuellement, les accès aux données et métadonnées ont été singés avec Rhino Mock pour éviter d'utiliser le système financier en COM Interop. Voilà donc de nombreuses bonnes raisons de s'intéresser à ce type de framework.

Syntaxe de Rhino Mock 3.5

Regardons maintenant de plus près à quoi cela ressemble.

J'ai pris comme exemple un appareil photo (Camera) qui utilise des objectifs (Lens). On veut développer l'appareil photo avant d'avoir développé un objectif. Pour les besoins du post, j'ai créé une interface qui représente les objectifs, et j'y ai mis la plupart des cas que l'on peut rencontrer, à savoir un événement, une propriété, une fonction qui retourne une valeur, et une fonction qui retourne des paramètres via out ou ref.

Ceux qui aiment bien la photo auront reconnu que Aperture est l'ouverture de l'objectif.

public interface ILens : INotifyPropertyChanged
{
    void ApertureRange(
        out double minAperture, 
        out double maxAperture);
    double CurrentAperture { get; set; }
    double FocusDistance{ get; set; }
    bool IsAutoFocus { get; }
    bool HasFeature(LensFeatures feature);
}
J'ai aussi utilisé l'énumération suivante :
public enum LensFeatures 
    {
        Stabilisator,
        Motorisation
    }

Maintenant que tout est en place, regardons comment singer l'interface :

Création du proxy :

//We generate a proxy for the ILens interface.
var lens = MockRepository.GenerateStub<ILens>();

Singer une fonction :

//We return different values according to the parameters.
lens.Stub( 
    myLens => myLens.HasFeature(LensFeatures.Stabilisator))
    .Return(false);

lens.Stub(
    myLens => myLens.HasFeature(LensFeatures.Motorisation))
    .Return(true);

Singer une fonction avec paramètres out ou ref :

//We can also return out and ref parameters.
lens.Stub(
    myLens =>
    {
        double myMinAperture, myMaxAperture;
        myLens.ApertureRange(
            out myMinAperture, 
            out myMaxAperture);
    }).OutRef(2.0D, 22D);

Singer une propriété :

//We can always return the same property value.
lens.Stub(
    myLens => myLens.IsAutoFocus)
    .Return(true).Repeat.Any();

Utiliser une propriété :

//The stub also provides storage for properties
lens.CurrentAperture = 10;

Singer un événement :

//Event can also be raised
lens.Raise(
    myLens => myLens.PropertyChanged += lens_PropertyChanged,
    lens, new PropertyChangedEventArgs("CurrentAperture"));

Test du proxy :

Le code de test suivant permet de voir les valeurs qui sont retournées par le proxy.

Assert.AreEqual(10, lens.CurrentAperture);

double minAperture, maxAperture; 
lens.ApertureRange(out minAperture, out maxAperture);
Assert.AreEqual(2.0, minAperture);
Assert.AreEqual(22, maxAperture);

Assert.IsFalse(lens.HasFeature(LensFeatures.Stabilisator));
Assert.IsTrue(lens.HasFeature(LensFeatures.Motorisation));

Assert.IsTrue(lens.IsAutoFocus);

Un peu plus d'explications :

La fonction MockRepository.GenerateStub<ILens>(); permet de créer un proxy à partir d'une interface. Cela fonctionne également avec une classe. Ainsi, si vous n'avez pas designé votre système avec une interface, vous pouvez quand même générer un proxy. Cela peut aussi être utile si vous n'êtes pas le créateur de la classe.

Dans la partie sur les fonctions, j'ai mis en évidence que l'on pouvait singer la même fonction avec des paramètres différents : c'est extrêmement utile. Dans notre exemple, on voit que notre objectif a une motorisation mais n'a pas de stabilisateur.

Dans la partie sur les propriétés, on voit apparaître .Repeat.Any(); cela permet de dire que l'on veut que la propriété retourne toujours la même valeur. Par défaut, le proxy ne la retourne qu'une seule fois. Cela peut poser des problèmes : par exemple, si on la regarde au débugger, cela va consommer la valeur et la suivante sera null. Il faut donc être vigilant à ce propos.

On voit aussi dans "Utiliser une propriété" que le proxy offre le support de la propriété sans avoir besoin d'ajouter du code. On peut donc affecter une valeur et l'utiliser ensuite.

L'événement est invoqué grâce à Raise. On n'est cependant pas obligé de mettre le handler de l'événement dans le Raise : il est possible de le mettre ailleurs et faire dans le Raise += null qui marche aussi.

De manière générale, la nouvelle syntaxe permet de décrire ce que l'on va faire grâce à une lambda et quand cette dernière se produit, le proxy retourne la valeur indiquée.

On peut également remettre à zéro le proxy grâce à BackToRecord();

Liens utiles :

Le site de l'auteur de Rhino Mock http://ayende.com/ : vous trouverez la page de download de la version 3.5 qui n'est actuellement qu'une beta, mais déjà stable. La page directe vers le projet est http://ayende.com/projects/rhino-mocks.aspx.

Voilà aussi une entrée utile sur son blog, qui décrit la syntaxe : Arrange Act Assert. Il y a aussi la page des tests de Rhino Mock qui permet de vraiment voir presque toute la syntaxe et son utilisation :  Rhino.Mocks.Tests.

Pour ceux qui voudraient voir d'autres frameworks, il y a aussi :

  • NMock
  • NMock2
  • Typemock .NET (utilise l'API de profiling : il permet d'intercepter pratiquement n'importe quel appel, même un HttpContext par exemple ;))
  • Easymock .Net 
  • Moq (gros buzz autour de ce framework, syntaxe complètement adaptée à .NET 3.5)

Conclusion :

Je trouve ce type de framework vraiment puissant et utile.

J'ai comparé un peu les frameworks avant de partir sur Rhino Mock. On peut noter que Typemock permet de faire des interceptions très puissantes grâce à l'API de profiling ; Moq a une syntaxe écrite spécialement pour tirer parti de .NET 3.5. Quant à Rhino Mock, il offre aussi une syntaxe .NET 3.5, il a plus de fonctionnalités et d'historique que Moq. Par rapport à TypeMock, il a l'avantage d'être open source. Je dirais donc que mon choix se résume à ces trois candidats :).

Dans mes projets, pour le moment, je n'utilise que les fonctionnalités de Rhino Mock que j'ai décrites dans l'article, et je trouve déjà cela très productif. Rhino Mock permet également d'exprimer des attentes (Expectations), et, à la fin du test, de voir si toutes ces attentes ont été réalisées.

Voilà, j'espère que ce post vous sera utile, et je ne saurais que trop vous encourager à tester, tester, tester :)

 

Happy programming !

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 21 septembre 2008 22:52 par fredhamel

Commentaires

tja a dit :

Superb article. Je viens de le voir.

# septembre 8, 2009 11:15
Les commentaires anonymes sont désactivés

Les 10 derniers blogs postés

- Merci par Blog de Jérémy Jeanson le 10-01-2019, 20:47

- Office 365: Script PowerShell pour auditer l’usage des Office Groups de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 11:02

- Office 365: Script PowerShell pour auditer l’usage de Microsoft Teams de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 10:39

- Office 365: Script PowerShell pour auditer l’usage de OneDrive for Business de votre tenant par Blog Technique de Romelard Fabrice le 04-25-2019, 15:13

- Office 365: Script PowerShell pour auditer l’usage de SharePoint Online de votre tenant par Blog Technique de Romelard Fabrice le 02-27-2019, 13:39

- Office 365: Script PowerShell pour auditer l’usage d’Exchange Online de votre tenant par Blog Technique de Romelard Fabrice le 02-25-2019, 15:07

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Stream Portal par Blog Technique de Romelard Fabrice le 02-21-2019, 17:56

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Video Portal par Blog Technique de Romelard Fabrice le 02-18-2019, 18:56

- Office 365: Script PowerShell pour extraire les Audit Log basés sur des filtres fournis par Blog Technique de Romelard Fabrice le 01-28-2019, 16:13

- SharePoint Online: Script PowerShell pour désactiver l’Option IRM des sites SPO non autorisés par Blog Technique de Romelard Fabrice le 12-14-2018, 13:01