将Microsoft.Extension.Loggin.Abstractions从2.0.0.0版本更新到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>>
类型为FormattedLogValues
类型。
在v2中,FormattedLogValues是一个公共类,但在v3.1.1中,FormatedLogValues却是一个内部只读结构。此更改似乎与测试失败有关。
我试过用It.IsAny代替IReadOnlyList或FormattedLogValues作为第三个参数,但我运气不好。
有人知道我如何修改这个测试代码以使测试通过吗?我可以从调试信息中看到,对Log方法的调用是按预期进行的,我只是不知道如何使用内部只读结构参数正确设置这些mock。
这是一个.NET Core 2.2项目。
.NET Core 3.*中的FormattedLogValues
被更改为内部,这有点不方便。使用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);
作为附带说明,您提到您正在设置记录器mock。我想不出有什么时候我明确地设置了一个记录器模拟,因为通常没有任何必要。
最后,如果你像我一样做这件事,通常是在无头黑框中,可以考虑contrib库Moq.Contrrib.ExpressionBuilders.Logging,它可以处理以上所有内容,并提供一个流畅的生成器来轻松创建验证表达式。免责声明,我是作者。