【问题标题】:Cannot verify a Moq'd Func is invoked无法验证已调用 Moq'd Func
【发布时间】:2026-01-15 00:50:01
【问题描述】:

我正在尝试使用 Moq 模拟 Func<Tin, Tout> 并验证它是否被调用(调用)

我目前拥有的代码是:

var handlers = new List<ICallBridgeHandler>();
var stationIdReader = new Mock<IStationIdReader>();
var loggerCreator = new Mock<Func<Type, ILogger>>();
loggerCreator.Setup(x => x.Invoke(typeof(Service))).Returns<ILogger>(null);

loggerCreator.Verify(x => x.Invoke(It.IsAny<Type>()), Times.Never);
var sut = new Service(stationIdReader.Object, handlers, loggerCreator.Object);
loggerCreator.Verify(x => x.Invoke(typeof(Service)), Times.Once);
Assert.IsNotNull(sut);

但这是一个例外:

Result StackTrace:  
at Moq.ExpressionExtensions.GetCallInfo(LambdaExpression expression, Mock mock)
at Moq.Mock.<>c__DisplayClass1c`2.<Setup>b__1b()
at Moq.PexProtector.Invoke[T](Func`1 function)
at Moq.Mock.Setup[T,TResult](Mock`1 mock, Expression`1 expression, Condition condition)
at Moq.Mock`1.Setup[TResult](Expression`1 expression)
at Tests.ConstructorRetrievesLoggerFromCreator() in ...

Result Message: 
Test method Tests.ConstructorRetrievesLoggerFromCreator threw exception: 
System.InvalidCastException: Unable to cast object of type
'System.Linq.Expressions.InstanceMethodCallExpressionN' to type
'System.Linq.Expressions.InvocationExpression'.

谁能帮忙解决这个问题,我需要能够检查构造函数是否调用了 Func。

注意我试图删除设置步骤,因为我不关心此测试的返回值,并且在验证调用时遇到了同样的问题。

显然我可以注入一个返回给定值的具体 Func 并以某种方式询问该值,但是我的 Service 类看起来像:

public class Service 
{
    private readonly ILogger _logger;

    [ImportingConstructor]
    public Service([Import("GetLogger")]Func<Type, ILogger> loggerCreator)
    {
        if (loggerCreator == null)
            throw new ArgumentNullException(nameof(loggerCreator));

        _logger = loggerCreator.Invoke(GetType());
    }
}

因此,如果不更改我的类,我看不到成员变量 _logger,因此我无法验证 loggerCreator 是否已被调用,这就是我想要验证的内容。

【问题讨论】:

  • 我不确定您是否可以起订量委托。
  • 另一个问题没有显示如何验证 Func 是否被调用,它允许您注入一个返回模拟对象的 Func,通过询问此返回值可以隐式验证 Func 是否被调用,但是(正如我的编辑中所解释的)因为我没有从我的 Service 类中公开 Func 的返回值,所以我不能使用这种方法来修改我的代码。

标签: c# unit-testing mocking moq


【解决方案1】:

无需模拟函数。创建一个Func 变量并在其中保留一个计数器以进行跟踪。

[TestMethod]
public void Func_Should_Be_Invoked_Once() {
    //Arrange
    var handlers = new List<ICallBridgeHandler>();
    var stationIdReader = new Mock<IStationIdReader>();
    var mockLogger = new Mock<ILogger>();
    int loggerCreaterInvokeCalls = 0;
    Func<Type, ILogger> loggerCreator = (type) => {
        loggerCreaterInvokeCalls++;
        return mockLogger.Object;
    };
    //Act
    var sut = new Service(stationIdReader.Object, handlers, loggerCreator);
    //Assert
    Assert.IsNotNull(sut);
    Assert.AreEqual(1, loggerCreaterInvokeCalls);
}

参考:Using Moq to Mock a Func<> constructor parameter and Verify it was called twice

【讨论】:

  • 很好的答案 起订量无关紧要,手动构建的间谍是最好的选择,谢谢@NKosi
  • @MikeW,没问题。您在注入混凝土Func 时走在了正确的轨道上,并且最终会找到正确的解决方案。