Как я могу использовать Moq для макетирования метода расширения? - PullRequest
74 голосов
/ 18 февраля 2009

Я пишу тест, который зависит от результатов метода расширения, но я не хочу, чтобы будущий сбой этого метода расширения когда-либо нарушал этот тест. Насмешка над этим результатом казалась очевидным выбором, но Moq, похоже, не предлагает способ переопределить статический метод (требование для метода расширения). Есть похожая идея с Moq.Protected и Moq.Stub, но они, похоже, ничего не предлагают для этого сценария. Я что-то упустил или я должен идти по этому другому пути?

Вот тривиальный пример, который завершается неудачно с обычным «Недопустимое ожидание для не переопределяемого члена» . Это плохой пример необходимости макетировать метод расширения, но он должен это делать.

public class SomeType {
    int Id { get; set; }
}

var ListMock = new Mock<List<SomeType>>();
ListMock.Expect(l => l.FirstOrDefault(st => st.Id == 5))
        .Returns(new SomeType { Id = 5 });

Что касается любых наркоманов TypeMock, которые могут предложить вместо меня использовать Isolator: я ценю усилия, так как похоже, что TypeMock может выполнять работу с завязанными глазами и опьянением, но наш бюджет в ближайшее время не увеличивается.

Ответы [ 5 ]

58 голосов
/ 18 февраля 2009

Методы расширения - это просто скрытые статические методы. Фальсифицирующие среды, такие как Moq или Rhinomocks, могут создавать только фиктивные экземпляры объектов, это означает, что имитировать статические методы невозможно.

18 голосов
/ 28 марта 2014

Если вы можете изменить код методов расширения, вы можете написать его следующим образом, чтобы иметь возможность проверить:

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;

public static class MyExtensions
{
    public static IMyImplementation Implementation = new MyImplementation();

    public static string MyMethod(this object obj)
    {
        return Implementation.MyMethod(obj);
    }
}

public interface IMyImplementation
{
    string MyMethod(object obj);
}

public class MyImplementation : IMyImplementation
{
    public string MyMethod(object obj)
    {
        return "Hello World!";
    }
}

Таким образом, методы расширения являются лишь оболочкой для интерфейса реализации.

(Вы можете использовать только класс реализации без методов расширения, которые являются своего рода синтаксическим сахаром.)

И вы можете смоделировать интерфейс реализации и установить его как реализацию для класса расширений.

public class MyClassUsingExtensions
{
    public string ReturnStringForObject(object obj)
    {
        return obj.MyMethod();
    }
}

[TestClass]
public class MyTests
{
    [TestMethod]
    public void MyTest()
    {
        // Given:
        //-------
        var mockMyImplementation = new Mock<IMyImplementation>();

        MyExtensions.Implementation = mockMyImplementation.Object;

        var myObject = new Object();
        var myClassUsingExtensions = new MyClassUsingExtensions();

        // When:
        //-------
        myClassUsingExtensions.ReturnStringForObject(myObject);

        //Then:
        //-------
        // This would fail because you cannot test for the extension method
        //mockMyImplementation.Verify(m => m.MyMethod());

        // This is success because you test for the mocked implementation interface
        mockMyImplementation.Verify(m => m.MyMethod(myObject));
    }
}
15 голосов
/ 29 апреля 2010

Я знаю, что этот вопрос не был активен в течение года, но Microsoft выпустила платформу для обработки именно этого названия, которая называется Родинок .

Вот также несколько учебных пособий:

DimeCasts.net Учебное пособие Николая Тиллмана
12 голосов
/ 01 октября 2010

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

public static class MyExtensions
{
    public static string MyExtension<T>(this T obj)
    {
        return "Hello World!";
    }
}

public interface IExtensionMethodsWrapper
{
    string MyExtension<T>(T myObj);
}

public class ExtensionMethodsWrapper : IExtensionMethodsWrapper
{
    public string MyExtension<T>(T myObj)
    {
        return myObj.MyExtension();
    }
}

Затем вы можете смоделировать методы-обертки в своих тестах и ​​код с помощью контейнера IOC.

4 голосов
/ 29 января 2015

Для методов расширения я обычно использую следующий подход:

public static class MyExtensions
{
    public static Func<int,int, int> _doSumm = (x, y) => x + y;

    public static int Summ(this int x, int y)
    {
        return _doSumm(x, y);
    }
}

Позволяет довольно легко вводить _doSumm.

...