我如何使用Moq来模拟扩展方法?

我正在编写一个testing,这个testing取决于扩展方法的结果,但是我不希望这个扩展方法的未来失败破坏这个testing。 嘲笑这个结果似乎是一个明显的select,但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 }); 

至于任何可能build议我使用隔离器的TypeMock瘾君子:我很欣赏这样的努力,因为它看起来像TypeMock可以做蒙住眼睛和醉酒的工作,但我们的预算不会很快增加。

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

如果你控制扩展方法的定义(即它们不是LINQ内置的),还有另一种方法,在http://blogs.clariusconsulting.net/kzu/making-extension-methods-amenable-to-嘲笑/ “制作延伸方法嘲弄” 1

我知道这个问题已经有一年多了,但是微软发布了一个框架来处理这个叫做Moles的问题 。

以下是一些教程:

  • DimeCasts.net
  • 尼古拉·蒂尔曼的教程
  • 我为我需要模拟的扩展方法创build了一个包装类。

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

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

    如果你可以改变扩展方法的代码,那么你可以这样编码,以便能够testing:

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

    对于扩展方法,我通常使用以下方法:

     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相当容易。