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


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

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

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 может выполнять работу с завязанными глазами и нетрезвым, но наш бюджет не увеличивается в любое время скоро.

6 63

6 ответов:

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

Если вы контролируете определение методов расширения (т. е. они не являются встроенными LINQ), есть еще одна альтернатива, объясненная в "делая методы расширения поддаются насмешкам"

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

вот несколько учебников, а также:

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

    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.

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

    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!";
        }
    }
    

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

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

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

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

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

    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 довольно легко.