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

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调用?


当前回答

在我的例子中,扩展方法是围绕我的类的一些公共方法的方法。我检查了内部方法的调用。这种方法类似于Alvis的答案(上面)。

其他回答

如果您只是想确保扩展方法被调用,并且不试图设置返回值,那么您可以检查被模拟对象上的Invocations属性。

是这样的:

var invocationsCount = mockedObject.Invocations.Count;
invocationsCount.Should().BeGreaterThan(0);

你不能用模拟框架“直接”模拟静态方法(因此是扩展方法)。你可以试试mole (http://research.microsoft.com/en-us/projects/pex/downloads.aspx),这是微软提供的一个免费工具,它实现了一种不同的方法。 以下是该工具的描述:

mole是一个基于委托的。net测试存根和弯路的轻量级框架。 mole可以用来绕过任何. net方法,包括密封类型中的非虚拟/静态方法。

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

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

它不是完美的,但是对于单元测试来说,它工作得很好。

在我的例子中,扩展方法是围绕我的类的一些公共方法的方法。我检查了内部方法的调用。这种方法类似于Alvis的答案(上面)。

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

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