250 votes

Mocking des méthodes d'extension avec Moq

J'ai une interface préexistante...

public interface ISomeInterface
{
    void SomeMethod();
}

et j'ai étendu cette interface en utilisant un mixin...

public static class SomeInterfaceExtensions
{
    public static void AnotherMethod(this ISomeInterface someInterface)
    {
        // Implementation here
    }
}

J'ai une classe qui appelle ceci et que je veux tester...

public class Caller
{
    private readonly ISomeInterface someInterface;

    public Caller(ISomeInterface someInterface)
    {
        this.someInterface = someInterface;
    }

    public void Main()
    {
        someInterface.AnotherMethod();
    }
}

et un test où je voudrais simuler l'interface et vérifier l'appel à la méthode d'extension...

    [Test]
    public void Main_BasicCall_CallsAnotherMethod()
    {
        // Arrange
        var someInterfaceMock = new Mock<ISomeInterface>();
        someInterfaceMock.Setup(x => x.AnotherMethod()).Verifiable();

        var caller = new Caller(someInterfaceMock.Object);

        // Act
        caller.Main();

        // Assert
        someInterfaceMock.Verify();
    }

L'exécution de ce test génère cependant une exception...

System.ArgumentException: Invalid setup on a non-member method:
x => x.AnotherMethod()

Ma question est la suivante : existe-t-il un moyen agréable de simuler l'appel à un mixin ?

4 votes

D'après mon expérience, les termes mixin et méthodes d'extension sont des choses distinctes. J'utiliserais le second dans ce cas pour éviter les confusions :P

3 votes

53voto

Alvis Points 384

J'ai utilisé un Wrapper pour contourner ce problème. Créez un objet wrapper et passez votre méthode simulée.

Voir Mocking des méthodes statiques pour les tests unitaires par Paul Irwin, il contient de bons exemples.

40voto

Mark Seemann Points 102767

Vous pouvez lire l'article du blog de Daniel Cazzulino (co-auteur du Moq). Comment simuler les méthodes d'extension .

Cependant, personnellement, je ne trouve pas ça "sympa" du tout...

40voto

Daniele Armanasco Points 1686

Il n'est pas possible de simuler "directement" une méthode statique (donc une méthode d'extension) avec un cadre de simulation. Vous pouvez essayer les moles ( http://research.microsoft.com/en-us/projects/pex/downloads.aspx ), un outil gratuit de Microsoft qui met en œuvre une approche différente. Voici la description de l'outil :

Moles est un cadre léger pour les stubs et les détours de test dans .NET qui est basé sur les délégués.

Les taupes peuvent être utilisées pour détourner toute méthode .NET, y compris les méthodes non virtuelles/statiques dans les types scellés.

Vous pouvez utiliser Moles avec n'importe quel cadre de test (il est indépendant à ce sujet).

2 votes

Outre Moles, il existe d'autres cadres de simulation (non libres) qui utilisent l'API du profileur de .NET pour simuler des objets et peuvent donc remplacer tous les appels. Les deux que je connais sont JustMock de Telerik et Isolateur TypeMock .

7 votes

Moles en théorie est bon, mais j'ai trouvé trois problèmes quand je l'ai testé qui m'ont empêché de l'utiliser... 1) Il ne fonctionne pas dans le runner NUnit de Resharper 2) Vous devez créer manuellement un assembly mole pour chaque assembly stubbed 3) Vous devez recréer manuellement un assembly mole à chaque fois qu'une méthode stubbed change.

29voto

chris31389 Points 15

J'ai découvert que je devais découvrir l'intérieur de la méthode d'extension pour laquelle j'essayais de simuler l'entrée, et simuler ce qui se passait à l'intérieur de l'extension.

Je considère que l'utilisation d'une extension revient à ajouter du code directement à votre méthode. Cela signifie que j'avais besoin de simuler ce qui se passe à l'intérieur de l'extension plutôt que l'extension elle-même.

5voto

Rhyous Points 2163

J'aime utiliser le wrapper (modèle d'adaptateur) lorsque j'enveloppe l'objet lui-même. Je ne suis pas sûr que je l'utiliserais pour envelopper une méthode d'extension, qui ne fait pas partie de l'objet.

J'utilise une propriété interne injectable paresseuse de type Action, Func, Prédicat ou Délégué et permet d'injecter (échanger) la méthode pendant un test unitaire.

    internal Func<IMyObject, string, object> DoWorkMethod
    {
        [ExcludeFromCodeCoverage]
        get { return _DoWorkMethod ?? (_DoWorkMethod = (obj, val) => { return obj.DoWork(val); }); }
        set { _DoWorkMethod = value; }
    } private Func<IMyObject, string, object> _DoWorkMethod;

Vous appelez alors le Func au lieu de la méthode réelle.

    public object SomeFunction()
    {
        var val = "doesn't matter for this example";
        return DoWorkMethod.Invoke(MyObjectProperty, val);
    }

Pour un exemple plus complet, consultez le site http://www.rhyous.com/2016/08/11/unit-testing-calls-to-complex-extension-methods/

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X