web-dev-qa-db-fra.com

Méthodes d'extension moqueuses 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 cela 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 me moquer de 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 bon moyen de se moquer de l’appel de mixin?

149
Russell Giddings

Vous ne pouvez pas "directement" simuler une méthode statique (d'où une méthode d'extension) avec le framework moqueur. Vous pouvez essayer Moles ( http://research.Microsoft.com/en-us/projects/pex/downloads.aspx ), un outil gratuit de Microsoft qui implémente une approche différente. Voici la description de l'outil:

Moles est un framework léger pour les talons de test et les détours dans .NET basé sur des 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 des types scellés.

Vous pouvez utiliser Moles avec n’importe quel framework de test (c’est indépendant à ce sujet).

30
Daniele Armanasco

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

Voir Méthodes statiques moqueuses pour les tests unitaires par Paul Irwin, il contient de beaux exemples.

18
Alvis

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

J'ai utilisé une extension pour ajouter du code directement à votre méthode. Cela signifiait que je devais me moquer de ce qui se passait dans l'extension plutôt que l'extension elle-même.

9
chris31389

Vous pouvez simuler une interface de test héritant de l'interface réelle et comportant un membre avec la même signature que la méthode d'extension.

Vous pouvez ensuite simuler l'interface de test, ajouter la vraie à la maquette et appeler la méthode de test dans la configuration.

Votre implémentation de la maquette peut alors appeler la méthode de votre choix ou simplement vérifier que la méthode s'appelle:

IReal //on which some extension method is defined
{
    ... SomeNotAnExtensionMethod(...);
}

ITest: IReal
{
    ... SomeExtensionMethod(...);
}

var someMock = new Mock<ITest>();
Mock.As<IReal>(); //ad IReal to the mock
someMock.Setup(x => x.SomeExtensionMethod()).Verifiable(); //Calls SomeExtensionMethod on ITest
someMock.As<IReal>().Setup(x => x.SomeNotAnExtensionMethod()).Verifiable(); //Calls SomeNotAnExtensionMethod on IReal

Merci à la solution Håvard S dans cet article pour la façon de mettre en œuvre une maquette qui prend en charge l’interface. Une fois que je l’ai trouvée, l’adapter à l’interface de test et à la méthode statique était une marche du gâteau.

3
pasx

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

J'utilise une propriété Lazy Injectable interne de type Action, Func, Predicate ou delegate et j'autorise l'injection (la permutation) de la méthode lors d'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;

Ensuite, vous appelez 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 http://www.rhyous.com/2016/08/11/11/unit-testing-calls-tocomplex-extension-methods/

2
Rhyous