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

pat*_*dge 79 c# extension-methods moq mocking

我正在编写一个依赖于扩展方法结果的测试,但我不希望该扩展方法的未来失败会破坏此测试.模拟这个结果似乎是显而易见的选择,但是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 });
Run Code Online (Sandbox Code Playgroud)

至于任何可能建议我使用Isolator的TypeMock迷们:我很欣赏这项努力,因为看起来TypeMock可能会被蒙住眼睛和醉酒,但我们的预算不会很快增加.

Men*_*elt 61

扩展方法只是伪装的静态方法.像Moq或Rhinomocks这样的模拟框架只能创建对象的模拟实例,这意味着无法模拟静态方法.

  • @ Mendelt ..然后,一个单元如何测试内部具有扩展方法的方法?有哪些可能的选择? (55认同)
  • 上面的链接已经死了。这是存档http://web.archive.org/web/20190914122636/http://agooddayforscience.blogspot.com:80/2017/08/mocking-extension-methods.html (6认同)
  • @Alexander我有同样的问题,然后这个答案非常棒:http://agooddayforscience.blogspot.com/2017/08/mocking-extension-methods.html - 看看这个,它是一个救星! (2认同)

inf*_*ius 22

如果您可以更改扩展方法代码,那么您可以像这样编码以便能够测试:

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!";
    }
}
Run Code Online (Sandbox Code Playgroud)

因此,扩展方法只是实现接口的包装.

(你可以只使用没有扩展方法的实现类,这些方法是一种语法糖.)

您可以模拟实现接口并将其设置为扩展类的实现.

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));
    }
}
Run Code Online (Sandbox Code Playgroud)


Mik*_*den 15

我知道这个问题大约一年没有激活,但是微软发布了一个框架来处理这个名为Moles的问题.

这里有一些教程:

  • DimeCasts.net
  • 尼古拉蒂尔曼的教程


    ran*_*sen 12

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

    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();
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

    然后,您可以使用IOC容器模拟测试中的包装器方法和代码.

    • 这就是OP的全部要点。这是一个解决方法。 (2认同)