Методы расширения с помощью Moq - PullRequest
147 голосов
/ 19 февраля 2010

У меня есть ранее существующий интерфейс ...

public interface ISomeInterface
{
    void SomeMethod();
}

и я расширил этот интерфейс, используя миксин ...

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

У меня есть класс, называющий это, который я хочу проверить ...

public class Caller
{
    private readonly ISomeInterface someInterface;

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

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

и тест, в котором я хотел бы смоделировать интерфейс и проверить вызов метода расширения ...

    [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();
    }

Выполнение этого теста, однако, создает исключение ...

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

У меня вопрос: есть ли хороший способ сделать миксин-звонок?

Ответы [ 5 ]

30 голосов
/ 23 августа 2011

Вы не можете "напрямую" макетировать статический метод (следовательно, метод расширения) с помощью фреймворка.Вы можете попробовать Moles (http://research.microsoft.com/en-us/projects/pex/downloads.aspx), - бесплатный инструмент от Microsoft, который реализует другой подход. Вот описание инструмента:

Moles - это облегченная среда для тестовых заглушек и обходов в.NET, основанный на делегатах.

Кроты можно использовать для обхода любого метода .NET, включая не виртуальные / статические методы в закрытых типах.

Кроты можно использовать слюбой механизм тестирования (он не зависит от этого).

17 голосов
/ 25 ноября 2014

Я использовал Обертку, чтобы обойти эту проблему.Создайте объект-обертку и передайте имитируемый метод.

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 1004
6 голосов
/ 07 марта 2017

Я обнаружил, что мне нужно обнаружить внутреннюю часть метода расширения, для которого я пытался смоделировать ввод, и смоделировать, что происходит внутри расширения.

Я рассматривал использование расширения как добавление кода непосредственно в ваш метод. Это означало, что мне нужно высмеивать то, что происходит внутри расширения, а не само расширение.

2 голосов
/ 07 марта 2019

Вы можете смоделировать тестовый интерфейс, который наследует от реального и имеет элемент с такой же сигнатурой, что и у метода расширения.

Затем можно смоделировать тестовый интерфейс, добавить реальный интерфейс в макет ивызовите метод test в настройке.

Ваша реализация макета может затем вызвать любой метод, который вы хотите, или просто проверить, что метод вызывается:

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

Благодаря решению Håvard Sв этом посте о том, как реализовать макет, поддерживающий интерфейс.Как только я нашел его, адаптировать его к тестовому интерфейсу и статическому методу было очень просто.

2 голосов
/ 12 августа 2016

Мне нравится использовать обертку (шаблон адаптера), когда я обертываю сам объект. Я не уверен, что использовал бы это для переноса метода расширения, который не является частью объекта.

Я использую внутреннее свойство Lazy Injectable, имеющее тип Action, Func, Predicate или делегат, и позволяю вводить (заменять) метод во время модульного теста.

    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;

Затем вы вызываете Func вместо фактического метода.

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

Для более полного примера, проверьте http://www.rhyous.com/2016/08/11/unit-testing-calls-to-complex-extension-methods/

...