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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/562129/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 08:15:59  来源:igfitidea点击:

How do I use Moq to mock an extension method?

c#mockingmoqextension-methods

提问by patridge

I am writing a test that depends on the results of an extension method but I don't want a future failure of that extension method to ever break this test. Mocking that result seemed the obvious choice but Moq doesn't seem to offer a way to override a static method(a requirement for an extension method). There is a similar idea with Moq.Protected and Moq.Stub, but they don't seem to offer anything for this scenario. Am I missing something or should I be going about this a different way?

我正在编写一个依赖于扩展方法结果的测试,但我不希望该扩展方法的未来失败会破坏这个测试。模拟该结果似乎是显而易见的选择,但Moq 似乎没有提供覆盖静态方法的方法(扩展方法的要求)。Moq.Protected 和 Moq.Stub 也有类似的想法,但它们似乎没有为这种情况提供任何东西。我是否遗漏了什么,或者我应该以不同的方式解决这个问题?

Here is a trivial example that fails with the usual "Invalid expectation on a non-overridable member". This is a bad example of needing to mock an extension method, but it should do.

这是一个简单的示例,该示例因通常的“对不可覆盖成员的无效期望”而失败。这是需要模拟扩展方法的一个不好的例子,但它应该这样做。

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

As for any TypeMock junkies that might suggest I use Isolator instead: I appreciate the effort since it looks like TypeMock could do the job blindfolded and inebriated, but our budget isn't increasing any time soon.

至于任何可能建议我使用 Isolator 的 TypeMock 爱好者:我很欣赏这种努力,因为看起来 TypeMock 可以蒙着眼睛和醉酒完成这项工作,但我们的预算不会很快增加。

采纳答案by Mendelt

Extension methods are just static methods in disguise. Mocking frameworks like Moq or Rhinomocks can only create mock instances of objects, this means mocking static methods is not possible.

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

回答by Mike Fielden

I know this question hasn't been active for about a year but Microsoft released a framework to handle exactly this called Moles.

我知道这个问题已经有大约一年没有活跃了,但微软发布了一个框架来处理这个叫做Moles 的问题

Here are a few tutorials as well:

这里还有一些教程:

  • DimeCasts.net
  • DimeCasts.net
  • Nikolai Tillman's Tutorial

  • 尼古拉·蒂尔曼的教程

  • 回答by ranthonissen

    I created a wrapper class for the extension methods that I needed to mock.

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

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

    Then you can mock the wrapper methods in your tests and code with your IOC container.

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

    回答by informatorius

    If you can change the extension methods code then you can code it like this to be able to test:

    如果您可以更改扩展方法代码,那么您可以像这样编写代码以进行测试:

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

    So the extention methods are only a wrapper around the implementation interface.

    所以扩展方法只是实现接口的包装器。

    (You could use just the implementation class without extension methods which are sort of syntactic sugar.)

    (你可以只使用实现类而不使用扩展方法,这是一种语法糖。)

    And you can mock the implementation interface and set it as implementation for the extensions class.

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

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

    回答by dmigo

    For extension methods I normally use the following approach:

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

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

    It allows to inject _doSumm fairly easy.

    它允许相当容易地注入 _doSumm。