C# 使用 Moq 模拟扩展方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2295960/
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-07 01:11:31  来源:igfitidea点击:

Mocking Extension Methods with Moq

c#unit-testingmockingextension-methodsmoq

提问by Russell Giddings

I have a preexisting Interface...

我有一个预先存在的接口...

public interface ISomeInterface
{
    void SomeMethod();
}

and I've extended this intreface using a mixin...

我已经使用 mixin 扩展了这个接口......

public static class SomeInterfaceExtensions
{
    public static void AnotherMethod(this ISomeInterface someInterface)
    {
        // Implementation here
    }
}

I have a class thats calling this which I want to test...

我有一个班级叫这个我想测试......

public class Caller
{
    private readonly ISomeInterface someInterface;

    public Caller(ISomeInterface someInterface)
    {
        this.someInterface = someInterface;
    }

    public void Main()
    {
        someInterface.AnotherMethod();
    }
}

and a test where I'd like to mock the interface and verify the call to the extension method...

以及我想模拟接口并验证对扩展方法的调用的测试...

    [Test]
    public void Main_BasicCall_CallsAnotherMethod()
    {
        // Arrange
        var someInterfaceMock = new Mock<ISomeInterface>();
        someInterfaceMock.Setup(x => x.AnotherMethod()).Verifiable();

        var caller = new Caller(someInterfaceMock.Object);

        // Act
        caller.Main();

        // Assert
        someInterfaceMock.Verify();
    }

Running this test however generates an exception...

但是运行这个测试会产生一个异常......

System.ArgumentException: Invalid setup on a non-member method:
x => x.AnotherMethod()

My question is, is there a nice way to mock out the mixin call?

我的问题是,有没有一种很好的方法来模拟 mixin 调用?

回答by Daniele Armanasco

You can't "directly" mock static method (hence extension method) with mocking framework. You can try Moles (http://research.microsoft.com/en-us/projects/pex/downloads.aspx), a free tool from Microsoft that implements a different approach. Here is the description of the tool:

您不能使用模拟框架“直接”模拟静态方法(因此是扩展方法)。您可以尝试 Moles ( http://research.microsoft.com/en-us/projects/pex/downloads.aspx),这是 Microsoft 提供的免费工具,它实现了不同的方法。以下是该工具的说明:

Moles is a lightweight framework for test stubs and detours in .NET that is based on delegates.

Moles may be used to detour any .NET method, including non-virtual/static methods in sealed types.

Moles 是一个轻量级框架,用于 .NET 中基于委托的测试存根和绕行。

Moles 可用于绕开任何 .NET 方法,包括密封类型中的非虚拟/静态方法。

You can use Moles with any testing framework (it's independent about that).

您可以将 Moles 与任何测试框架一起使用(它与此无关)。

回答by Alvis

I have used a Wrapper to get around this problem. Create a wrapper object and pass your mocked method.

我使用了 Wrapper 来解决这个问题。创建一个包装对象并传递您的模拟方法。

See Mocking Static Methods for Unit Testingby Paul Irwin, it has nice examples.

参见Paul Irwin 的Mocking Static Methods for Unit Testing,它有很好的例子。

回答by Rhyous

I like to use the wrapper (adapter pattern) when I am wrapping the object itself. I'm not sure I'd use that for wrapping an extension method, which is not part of the object.

当我包装对象本身时,我喜欢使用包装器(适配器模式)。我不确定我会用它来包装扩展方法,它不是对象的一部分。

I use an internal Lazy Injectable Property of either type Action, Func, Predicate, or delegate and allow for injecting (swapping out) the method during a unit test.

我使用 Action、Func、Predicate 或委托类型的内部惰性可注入属性,并允许在单元测试期间注入(换出)该方法。

    internal Func<IMyObject, string, object> DoWorkMethod
    {
        [ExcludeFromCodeCoverage]
        get { return _DoWorkMethod ?? (_DoWorkMethod = (obj, val) => { return obj.DoWork(val); }); }
        set { _DoWorkMethod = value; }
    } private Func<IMyObject, string, object> _DoWorkMethod;

Then you call the Func instead of the actual method.

然后调用 Func 而不是实际的方法。

    public object SomeFunction()
    {
        var val = "doesn't matter for this example";
        return DoWorkMethod.Invoke(MyObjectProperty, val);
    }

For a more complete example, check out http://www.rhyous.com/2016/08/11/unit-testing-calls-to-complex-extension-methods/

有关更完整的示例,请查看http://www.rhyous.com/2016/08/11/unit-testing-calls-to-complex-extension-methods/

回答by chris31389

I found that I had to discover the inside of the extension method I was trying to mock the input for, and mock what was going on inside the extension.

我发现我必须发现我试图模拟输入的扩展方法的内部,并模拟扩展内部发生的事情。

I viewed using an extension as adding code directly to your method. This meant I needed to mock what happens inside the extension rather than the extension itself.

我将使用扩展视为直接向您的方法添加代码。这意味着我需要模拟扩展内部发生的事情,而不是扩展本身。

回答by pasx

You can mock a test interface that inherits from the real one and has a member with the same signature as the extension method.

您可以模拟一个测试接口,该接口从真实接口继承并且具有与扩展方法具有相同签名的成员。

You can then mock the test interface, add the real one to the mock and call the test method in the setup.

然后,您可以模拟测试接口,将真实的接口添加到模拟中并调用设置中的测试方法。

Your implementation of the mock can then call whatever method you want or simply check the that the method is called:

然后您的模拟实现可以调用您想要的任何方法,或者简单地检查该方法是否被调用:

IReal //on which some extension method is defined
{
    ... SomeNotAnExtensionMethod(...);
}

ITest: IReal
{
    ... SomeExtensionMethod(...);
}

var someMock = new Mock<ITest>();
Mock.As<IReal>(); //ad IReal to the mock
someMock.Setup(x => x.SomeExtensionMethod()).Verifiable(); //Calls SomeExtensionMethod on ITest
someMock.As<IReal>().Setup(x => x.SomeNotAnExtensionMethod()).Verifiable(); //Calls SomeNotAnExtensionMethod on IReal

Thanks to H?vard S solution in this postfor how to implement a mock that supports to interface. Once I found it, adapting it with the test interface and the static method was a cake walk.

感谢这篇文章中的H?vard S 解决方案如何实现支持接口的模拟。一旦我找到它,将它与测试界面和静态方法相适应就变得轻而易举了。

回答by Mihail Vladov

You can easily mock an extension method with JustMock. The API is the same as mocking normal method. Consider the following

您可以使用JustMock轻松模拟扩展方法。API 与模拟普通方法相同。考虑以下

public static string Echo(this Foo foo, string strValue) 
{ 
    return strValue; 
}

To arrange and verify this method use the following:

要安排和验证此方法,请使用以下内容:

string expected = "World";

var foo = new Foo();
Mock.Arrange(() => foo.Echo(Arg.IsAny<string>())).Returns(expected);

string result = foo.Echo("Hello");

Assert.AreEqual(expected, result);

Here is also a link to the documentation: Extension Methods Mocking

这里也是文档的链接:扩展方法模拟

回答by ckernel

So if you are using Moq, and want to mock the result of an Extension method, then you can use SetupReturnsDefault<ReturnTypeOfExtensionMethod>(new ConcreteInstanceToReturn())on the instance of the mock class that has the extension method you are trying to mock.

因此,如果您正在使用 Moq,并且想要模拟 Extension 方法的结果,那么您可以SetupReturnsDefault<ReturnTypeOfExtensionMethod>(new ConcreteInstanceToReturn())在具有您尝试模拟的扩展方法的模拟类的实例上使用。

It is not perfect, but for unit testing purposes it works well.

它并不完美,但对于单元测试而言,它运行良好。