C# Moq模拟扩展方法

C# Moq模拟扩展方法,c#,unit-testing,mocking,extension-methods,moq,C#,Unit Testing,Mocking,Extension Methods,Moq,我有一个预先存在的接口 public interface ISomeInterface { void SomeMethod(); } 我用一个mixin扩展了这个intreface public static class SomeInterfaceExtensions { public static void AnotherMethod(this ISomeInterface someInterface) { // Implementation here

我有一个预先存在的接口

public interface ISomeInterface
{
    void SomeMethod();
}
我用一个mixin扩展了这个intreface

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();
    }
我的问题是,有没有一种模拟mixin调用的好方法?

您不能用模拟框架“直接”模拟静态方法(因此是扩展方法)。您可以尝试Moles(),这是一个来自微软的免费工具,它实现了一种不同的方法。 以下是该工具的说明:

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

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


您可以在任何测试框架中使用Moles(这是独立的)。

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

正如Paul Irwin所说,它有很好的例子。

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

我使用类型为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;

要获得更完整的示例,请查看

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


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

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


它并不完美,但出于单元测试的目的,它工作得很好

根据我的经验,术语mixin和扩展方法是分开的。在本例中,我将使用后者来避免混淆:PDuplicate of.。除了Moles之外,还有其他(非免费)模拟框架使用.NET的profiler API来模拟对象,因此可以替换任何调用。我知道的两个是和。摩尔理论上是好的,但我在试用它时发现了三个问题阻止了我使用它。。。1) 它不会在Resharper NUnit runner中运行2)您需要为每个存根程序集手动创建一个mole程序集3)每当存根方法更改时,您需要手动重新创建一个mole程序集。这很好,但是读者应该知道DoWorkMethod是类的一个新字段,该类的每个实例现在必须再分配一个字段。这种情况很少发生,但有时它确实会发生,这取决于您在任何时候分配的实例数。您可以通过将_DoWorkMethod设置为静态来解决此问题。这样做的缺点是,如果单元测试同时运行,两个可能修改相同静态值的不同单元测试将完成。我喜欢这个答案,因为它说的是(不直接说),您需要修改代码以使其可测试。这就是它的工作原理。要明白,在微芯片/IC/ASIC设计中,这些芯片不仅要设计为可以工作,还要进一步设计为可以测试,因为如果你不能测试一个微芯片,它是无用的——你不能保证它可以工作。软件也是如此。如果你还没有建立它是可测试的,它是。。。没用。将它构建为可测试的,这在某些情况下意味着重写代码(并使用包装器),然后构建测试它的自动化测试。我创建了一个小型库,它包装了Dapper、Dapper.Contrib和IDbConnection。我相信SetReturnsDefault()永远不会返回具体实例。如果是自定义c#类,则为空!这并不总是可能的。例如,当使用非开源扩展时,您没有编写。
    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;
    public object SomeFunction()
    {
        var val = "doesn't matter for this example";
        return DoWorkMethod.Invoke(MyObjectProperty, val);
    }