0

在将 Microsoft.Extensions.Logging.Abstractions 从 Version=2.0.0.0 更新到 Version=3.1.1.0 后,我的一些单元测试失败了。

我有一个 ILogger 被嘲笑为:

var loggerMock = new Mock<ILogger<BillingEventProcessor>>();            
loggerMock.Setup(l => l.Log(
It.IsAny<LogLevel>(), 
It.IsAny<EventId>(), 
It.IsAny<IReadOnlyList<KeyValuePair<string, object>>>(), 
It.IsAny<Exception>(), 
It.IsAny<Func<object, Exception, string>>()));

并且已经进行了验证对 Log() 的调用的单元:

logger.Verify(l => l.Log(
It.IsAny<LogLevel>(), 
It.IsAny<EventId>(),
It.IsAny<IReadOnlyList<KeyValuePair<string, object>>>(),
It.IsAny<Exception>(), 
It.IsAny<Func<object, Exception, string>>())
);

在更新之前,IReadOnlyList<KeyValuePair<string, object>>类型是 type FormattedLogValues

在 v2 中,FormattedLogValues 是一个公共类,但在 v3.1.1 中,FormattedLogValues 是一个内部只读结构。此更改似乎与测试失败有关。

我已经尝试将 It.IsAny 用于第三个参数,而不是 IReadOnlyList 或 FormattedLogValues,但我没有任何运气。

任何人都知道如何更改此测试代码以使测试通过?我可以从调试信息中看到对 Log 方法的调用按预期进行,我只是无法弄清楚如何使用内部只读结构参数正确设置这些模拟。

这是一个 .NET Core 2.2 项目。

4

1 回答 1

0

FormattedLogValues在 .NET Core 3.* 中更改为 internal 有点不方便。使用 Moq 的常见解决方案是使用It.IsAnyType

loggerMock.Verify(l => l.Log(
    It.IsAny<LogLevel>(),
    It.IsAny<EventId>(),
    It.IsAny<It.IsAnyType>(),
    It.IsAny<Exception>(),
    (Func<It.IsAnyType, Exception, string>)It.IsAny<object>()),
  Times.Once);

我通常喜欢从我的验证中获得更多信息,至少检查一下消息:

loggerMock.Verify(l => l.Log(
    It.IsAny<LogLevel>(),
    It.IsAny<EventId>(),
    It.Is<It.IsAnyType>((o, t) => ((IReadOnlyList<KeyValuePair<string, object>>)o).Last().Value.ToString().Equals(message)),
    It.IsAny<Exception>(),
    (Func<It.IsAnyType, Exception, string>)It.IsAny<object>()),
  Times.Once);

作为旁注,您提到您正在设置记录器模拟。我想不出有什么时候我明确地设置了一个记录器模拟,因为通常没有任何必要。

最后,如果您像我一样经常这样做,通常在无头黑匣子中,请考虑使用 contrib 库Moq.Contrib.ExpressionBuilders.Logging处理上述所有内容并提供流利的构建器来轻松创建验证表达式。免责声明,我是作者。

于 2020-05-11T23:49:31.097 回答