使用 Moq 模拟扩展方法
我有一个预先存在的接口...
public interface ISomeInterface
{
void SomeMethod();
}
并且我已经使用 mixin 扩展了这个接口...
public static class SomeInterfaceExtensions
{
public static void AnotherMethod(this ISomeInterface someInterface)
{
// Implementation here
}
}
我有一个调用它的类,我想测试它...
public class Caller
{
private readonly ISomeInterface someInterface;
public Caller(ISomeInterface someInterface)
{
this.someInterface = someInterface;
}
public void Main()
{
someInterface.AnotherMethod();
}
}
以及一个我想模拟接口的测试并验证对扩展方法的调用...
[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();
}
但是运行此测试会生成异常...
System.ArgumentException: Invalid setup on a non-member method:
x => x.AnotherMethod()
我的问题是:有没有一种很好的方法来模拟 mixin 调用?
I have a pre-existing Interface...
public interface ISomeInterface
{
void SomeMethod();
}
and I've extended this interface using a 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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
我使用了 Wrapper 来解决这个问题。创建一个包装对象并传递您的模拟方法。
请参阅模拟单元测试的静态方法作者:Paul Irwin,其中有很好的例子。
I have used a Wrapper to get around this problem. Create a wrapper object and pass your mocked method.
See Mocking Static Methods for Unit Testing by Paul Irwin, it has nice examples.
您不能使用模拟框架“直接”模拟静态方法(因此是扩展方法)。您可以尝试 Moles (http://research.microsoft.com/en- us/projects/pex/downloads.aspx),Microsoft 的免费工具,实现了不同的方法。
以下是该工具的描述:
您可以将 Moles 与任何测试框架一起使用(它是独立的)。
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:
You can use Moles with any testing framework (it's independent about that).
我发现我必须发现我试图模拟输入的扩展方法的内部,并模拟扩展内部发生的事情。
我将使用扩展视为直接将代码添加到您的方法中。这意味着我需要模拟扩展内部发生的事情,而不是扩展本身。
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.
如果您只想确保调用了扩展方法,并且不尝试设置返回值,那么您可以检查模拟对象上的
Initations
属性。像这样:
If you just want to make sure that the extension method was invoked, and you aren't trying to setup a return value, then you can check the
Invocations
property on the mocked object.Like this:
当我包装对象本身时,我喜欢使用包装器(适配器模式)。我不确定是否会使用它来包装扩展方法,该方法不是对象的一部分。
我使用 Action、Func、Predicate 或 delegate 类型的内部惰性可注入属性,并允许在单元测试期间注入(交换)方法。
然后您调用 Func 而不是实际的方法。
有关更完整的示例,请查看 http://www.rhyous.com/2016/08/11/unit-testing-calls-to-complex-extension-methods/
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.
Then you call the Func instead of the actual method.
For a more complete example, check out http://www.rhyous.com/2016/08/11/unit-testing-calls-to-complex-extension-methods/
好的答案中已经给出了无法模拟扩展方法的原因。我只是想用这个答案给出另一个可能的解决方案:通过调用扩展方法提取受保护的虚拟方法,并使用代理在测试类/方法中创建此方法的设置。
并测试
Reason why it is not possible to mock an extension method is already given in good answers. I am just trying to give another possible solution with this answer: Extract a protected, virtual method with the call to the extension method and create a setup for this method in the test class/method by using a proxy.
and test
就我而言,扩展方法是围绕我的类的某些公共方法的方法。所以我检查了该内部方法的调用。这种方法类似于阿尔维斯的回答(上面)。
In my case extension method is a method around some public method of my class. So I checked call of that internal method. That approach is similar to Alvis answer (above).
因此,如果您使用 Moq,并且想要模拟扩展方法的结果,那么您可以在具有扩展名的模拟类的实例上使用
SetupReturnsDefault(new ConcreteInstanceToReturn())
您试图模拟的方法。它并不完美,但对于单元测试来说它工作得很好。
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.It is not perfect, but for unit testing purposes it works well.