Rus*_*ngs 153 c# extension-methods unit-testing moq mocking
我有一个预先存在的界面......
public interface ISomeInterface
{
void SomeMethod();
}
Run Code Online (Sandbox Code Playgroud)
并且我使用mixin扩展了这个表面...
public static class SomeInterfaceExtensions
{
public static void AnotherMethod(this ISomeInterface someInterface)
{
// Implementation here
}
}
Run Code Online (Sandbox Code Playgroud)
我有一个叫这个我要测试的课程...
public class Caller
{
private readonly ISomeInterface someInterface;
public Caller(ISomeInterface someInterface)
{
this.someInterface = someInterface;
}
public void Main()
{
someInterface.AnotherMethod();
}
}
Run Code Online (Sandbox Code Playgroud)
和测试,我想模拟界面并验证对扩展方法的调用...
[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();
}
Run Code Online (Sandbox Code Playgroud)
然而,运行此测试会产生异常......
System.ArgumentException: Invalid setup on a non-member method:
x => x.AnotherMethod()
Run Code Online (Sandbox Code Playgroud)
我的问题是,有一个很好的方法来模拟混合调用吗?
Dan*_*sco 30
你不能用模拟框架"直接"模拟静态方法(因此扩展方法).您可以尝试Moles(http://research.microsoft.com/en-us/projects/pex/downloads.aspx),这是Microsoft提供的一种免费工具,可以实现不同的方法.以下是该工具的说明:
Moles是.NET中基于委托的测试存根和绕道的轻量级框架.
Moles可用于绕过任何.NET方法,包括密封类型中的非虚拟/静态方法.
您可以将Moles与任何测试框架一起使用(它与此无关).
Alv*_*vis 18
我用过Wrapper来解决这个问题.创建一个包装器对象并传递您的模拟方法.
请参阅Paul Irwin的" 用于单元测试的模拟静态方法",它有很好的例子.
chr*_*389 10
我发现我必须发现我试图模拟输入的扩展方法的内部,并模拟扩展内部的内容.
我查看使用扩展名直接将代码添加到您的方法中.这意味着我不需要模拟扩展内部发生的事情而不是扩展本身.
小智 7
如果您只想确保调用了扩展方法,并且不尝试设置返回值,那么您可以检查Invocations
模拟对象上的属性。
像这样:
var invocationsCount = mockedObject.Invocations.Count;
invocationsCount.Should().BeGreaterThan(0);
Run Code Online (Sandbox Code Playgroud)
当我包装对象本身时,我喜欢使用包装器(适配器模式)。我不确定是否会使用它来包装扩展方法,该方法不是对象的一部分。
我使用 Action、Func、Predicate 或 delegate 类型的内部惰性可注入属性,并允许在单元测试期间注入(交换)方法。
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;
Run Code Online (Sandbox Code Playgroud)
然后您调用 Func 而不是实际的方法。
public object SomeFunction()
{
var val = "doesn't matter for this example";
return DoWorkMethod.Invoke(MyObjectProperty, val);
}
Run Code Online (Sandbox Code Playgroud)
有关更完整的示例,请查看http://www.rhyous.com/2016/08/11/unit-testing-calls-to-complex-extension-methods/
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
Run Code Online (Sandbox Code Playgroud)
Thanks to Håvard S solution in this post for 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.
归档时间: |
|
查看次数: |
92835 次 |
最近记录: |