使用Moq模拟Func<>;构造函数参数,并验证它被调用了两次

本文关键字:调用 两次 验证 lt Func 模拟 Moq gt 使用 参数 构造函数 | 更新日期: 2023-09-27 17:58:11

根据本文中的问题(如何创建Func)进行调整,因为答案不正确。

public class FooBar
{
    private Func<IFooBarProxy> __fooBarProxyFactory;
    public FooBar(Func<IFooBarProxy> fooBarProxyFactory)
    {
        _fooBarProxyFactory = fooBarProxyFactory;
    }
    public void Process() 
    {
        _fooBarProxyFactory();
        _fooBarProxyFactory();
    }
}

我需要模仿Func<>作为构造函数参数传递,断言func被调用了两次。

当尝试模拟函数var funcMock = new Mock<Func<IFooBarProxy>>();Moq时,由于Func类型不可模拟,因此引发和异常。

问题是,如果不模拟func,就无法验证func是否被调用了(n)次。funcMock.Verify( (), Times.AtLeast(2));

使用Moq模拟Func<>;构造函数参数,并验证它被调用了两次

我认为没有必要为Func使用mock。

您可以简单地自己创建一个普通的Func,返回IFooBarProxy:的模拟

int numberOfCalls = 0;
Func<IFooBarProxy> func = () => { ++numberOfCalls;
                                  return new Mock<IFooBarProxy>(); };
var sut = new FooBar(func);
sut.Process();
Assert.Equal(2, numberOfCalls);

至少从Moq 4.5.28开始,你可以像预期的那样模拟和验证Func。我不知道这个功能是什么时候添加的(根据最初的问题,在某个时候这不起作用)。

[Test]
public void TestFoobar()
{
    var funcMock = new Mock<Func<IFooBarProxy>>();
    var fooBar = new FooBar(funcMock.Object);
    fooBar.Process();
    funcMock.Verify(x => x(), Times.AtLeast(2));
}

自Moq版本4.1.1308.2120

这个版本是在提出这个问题几个月后发布的(2013年8月21日),添加了模拟Func<>的功能。因此,对于任何当前版本的mock,都可以使用var funcMock = new Mock<Func<IFooBarProxy>>();

原始(过时)答案

如果有很多回调FuncActions等,最好在测试中定义一个助手接口并模拟该接口。通过这种方式,您可以使用常规的Moq功能,如设置返回值、测试输入参数等。

interface IFooBarTestMethods
{
    IFooBarProxy FooBarProxyFactory();
}

用法

var testMethodsMock = new Mock<IFooBarTestMethods>();
testMethodsMock
    .Setup(x => x.FooBarProxyFactory())
    .Returns(new Mock<IFooBarProxy>());
var sut = new FooBar(testMethodsMock.Object.FooBarProxyFactory);
testMethodsMock.Verify(x => x.FooBarProxyFactory(), Times.Exactly(2));

对于下面的这样的复杂场景

 public interface ISqlConnector
    {
        Task<int> ConnectAsync(Func<IDbConnection, Task<int>> performDatabaseOperationAsync, string connectionString);
    }

你可以使用

 var funcMock = new Mock<Func<IDbConnection,Task<int>>>();
            //setup Mock
            sqlConnector.Setup(a => a.ConnectAsync(funcMock.Object, It.IsAny<string>()));