Как использовать Moq для издевательства над методом расширения?

Я пишу тест, который зависит от результатов метода расширения, но я не хочу, чтобы в будущем этот метод расширения никогда не нарушал этот тест. Издевательство над этим результатом казалось очевидным выбором, но 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, которые могут предложить использовать изолятор вместо этого: я ценю усилия, поскольку похоже, что TypeMock может выполнять работу с завязанными глазами и опьяненными, но наш бюджет не увеличивается в ближайшее время.

Ответ 1

Методы расширения - это просто скрытые методы. Издевательские фреймворки, такие как Moq или Rhinomocks, могут создавать только ложные экземпляры объектов, это означает, что mocking static methods невозможно.

Ответ 3

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

Вот несколько учебников:

DimeCasts.net Учебник Никола Тильмана

Ответ 4

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

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.

Ответ 5

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

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

Ответ 6

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

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.