C# 如何使用Moq模拟扩展方法?

C# 如何使用Moq模拟扩展方法?,c#,mocking,moq,extension-methods,C#,Mocking,Moq,Extension Methods,我正在编写一个依赖于扩展方法结果的测试,但我不希望该扩展方法将来的失败会破坏这个测试。模拟这个结果似乎是一个显而易见的选择,但Moq似乎并没有提供一种覆盖静态方法的方法(对扩展方法的要求)。Moq.Protected和Moq.Stub也有类似的想法,但它们似乎没有为这种场景提供任何东西。我是错过了什么,还是应该换一种方式 下面是一个简单的例子,它失败于通常的“对不可重写成员的无效期望”。这是一个需要模拟扩展方法的坏例子,但它应该这样做 public class SomeType { in

我正在编写一个依赖于扩展方法结果的测试,但我不希望该扩展方法将来的失败会破坏这个测试。模拟这个结果似乎是一个显而易见的选择,但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 });
公共类SomeType{
int Id{get;set;}
}
var ListMock=new Mock();
Expect(l=>l.FirstOrDefault(st=>st.Id==5))
.Returns(新的SomeType{Id=5});

对于那些TypeMock的瘾君子,他们可能会建议我改用隔离器:我很感谢他们的努力,因为看起来TypeMock可以蒙住眼睛,醉醺醺地完成这项工作,但我们的预算不会很快增加。

扩展方法只是变相的静态方法。Moq或rhinomock等模拟框架只能创建对象的模拟实例,这意味着模拟静态方法是不可能的。

扩展方法只是伪装的静态方法。Moq或rhinomock等模拟框架只能创建对象的模拟实例,这意味着模拟静态方法是不可能的。

我知道这个问题已经有一年没有出现了,但微软发布了一个框架来处理这个问题

这里还有一些教程:

  • 我知道这个问题已经有一年没有出现了,但是微软发布了一个框架来处理这个问题,这个框架叫做

    这里还有一些教程:

  • 我为需要模拟的扩展方法创建了一个包装类

    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();
        }
    }
    
    公共静态类MyExtensions
    {
    公共静态字符串MyExtension(此T对象)
    {
    返回“你好,世界!”;
    }
    }
    公共接口IExtensionMethodsRapper
    {
    字符串MyExtension(T myObj);
    }
    公共类ExtensionMethodsWrapper:IExtensionMethodsWrapper
    {
    公共字符串MyExtension(T myObj)
    {
    返回myObj.MyExtension();
    }
    }
    

    然后,您可以在测试中模拟包装器方法,并使用IOC容器编写代码。

    我为需要模拟的扩展方法创建了一个包装器类

    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();
        }
    }
    
    公共静态类MyExtensions
    {
    公共静态字符串MyExtension(此T对象)
    {
    返回“你好,世界!”;
    }
    }
    公共接口IExtensionMethodsRapper
    {
    字符串MyExtension(T myObj);
    }
    公共类ExtensionMethodsWrapper:IExtensionMethodsWrapper
    {
    公共字符串MyExtension(T myObj)
    {
    返回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 myClassUsingExtensions = new MyClassUsingExtensions();
    
            // When:
            //-------
            var myObject = new Object();
            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));
        }
    }
    
    公共类MyClassUsingExtensions
    {
    公共字符串ReturnStringForObject(对象obj)
    {
    返回obj.MyMethod();
    }
    }
    [测试类]
    公共类MyTests
    {
    [测试方法]
    公共无效MyTest()
    {
    //鉴于:
    //-------
    var mockMyImplementation=new Mock();
    MyExtensions.Implementation=mockMyImplementation.Object;
    var myClassUsingExtensions=新的myClassUsingExtensions();
    //当:
    //-------
    var myObject=新对象();
    myClassUsingExtensions.ReturnStringForObject(myObject);
    //然后:
    //-------
    //这将失败,因为您无法测试扩展方法
    //验证(m=>m.MyMethod());
    //这是成功的,因为您测试了模拟的实现接口
    验证(m=>m.MyMethod(myObject));
    }
    }
    
    如果您可以更改扩展方法代码,那么您可以这样编码,以便能够测试:

    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 myClassUsingExtensions = new MyClassUsingExtensions();
    
            // When:
            //-------
            var myObject = new Object();
            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));
        }
    }
    
    公共类MyClassUsingExtensions
    {
    公共字符串ReturnStringForObject(对象obj)
    {
    返回obj.MyMethod();
    }
    }
    [测试类]
    公共类MyTests
    {
    [测试方法]
    公共无效MyTest()
    {
    //鉴于:
    //-------
    var mockMyImplementation=new Mock();
    MyExtensions.Implementation=mockMyImplementation.Object;
    var myClassUsingExtensions=新的myClassUsingExtensions();
    //当:
    //-------
    var myObject=新对象();
    myClassUsingExtensions.ReturnStringForObject(myObject);
    //然后:
    //-------
    //这将失败,因为您无法测试扩展方法
    //验证(m=>m.MyMethod());
    //这是成功的,因为您测试了模拟的实现接口
    验证(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);
        }
    }
    
    公共静态类MyExtensions
    {
    公共静态函数(x,y)=>x+y;
    公共静态整数总和(此整数x,整数y)
    {
    返回_doSumm(x,y);
    }
    }
    
    它允许相当容易地注入doSumm。

    用于扩展