我正在尝试通过 Moq 在我的项目(.net core 3.1)中测试 ILogger(Microsoft.Extensions.Logging),但是...它失败了..我试图捕获代码中的异常:
try{
//do something
}
catch (Exception e)
{
_logger.LogError(new
{
RequestId = requestId.ToString(),
Topic = "Merge files",
Message = "process failed",
Status = "Failed"
}, e);
}
Run Code Online (Sandbox Code Playgroud)
我定义了模拟记录器
private readonly Mock<ILogger<MyClass>> _logger = new Mock<ILogger<MyClass>>();
Run Code Online (Sandbox Code Playgroud)
这是我的 UT
_logger.Verify(
x => x.Log(
It.IsAny<LogLevel>(),
It.IsAny<EventId>(),
It.Is<It.IsAnyType>((v, t) => true),
It.IsAny<Exception>(),
It.Is<Func<It.IsAnyType, Exception, string>>((v, t) => true)), Times.Once);
Run Code Online (Sandbox Code Playgroud)
我调试了它,在我的代码中执行了日志异常方法,但最后 UT 失败,因为我收到以下消息:
预期在模拟上调用一次,但实际调用次数为 0 次: x => x.Log<It.IsAnyType>(It.IsAny(), It.IsAny(), It.Is<It.IsAnyType>((v, t) => True), It.IsAny(), It.Is<Func<It.IsAnyType, Exception, string>>((v, t) => True))
看起来您没有发布工作示例;我假设您的匿名对象是您的日志消息,并且您使用的是本机 LogError 扩展而不是您自己的扩展,这意味着异常参数需要位于匿名对象之前(也可能需要转换为字符串) 。
无论如何,解决了您提供的验证表达式在测试时确实有效的问题。LINQPad 示例:
void Main()
{
var loggerMock = new Mock<ILogger<Foo>>();
var logger = loggerMock.Object;
var requestId = Guid.NewGuid();
try
{
throw new InvalidOperationException("Bar");
}
catch (Exception ex)
{
logger.LogError(ex, new
{
RequestId = requestId.ToString(),
Topic = "Merge files",
Message = "process failed",
Status = "Failed"
}.ToString());
}
loggerMock.Verify(
x => x.Log(
It.IsAny<LogLevel>(),
It.IsAny<EventId>(),
It.Is<It.IsAnyType>((v, t) => true),
It.IsAny<Exception>(),
It.Is<Func<It.IsAnyType, Exception, string>>((v, t) => true)), Times.Once);
}
public class Foo
{
}
Run Code Online (Sandbox Code Playgroud)
更改为 Times.Never 并按预期失败:
您的测试是否调用了 SUT 中的该代码块?
一些额外的建议:
更好的做法是在日志消息中使用占位符:
logger.LogError(ex, "Process failed; requestId: '{requestId}', topic: '{topic}', status: '{status}'", requestId.ToString(), "Merge files", "Failed");
Run Code Online (Sandbox Code Playgroud)
然后记录的值(如果合适的话可以是对象)可以由日志聚合器解析。
断言日志调用可能很痛苦,有几个库可以让它变得更容易一些。Moq.Contrib.ExpressionBuilders.Logging是我专门编写的,因为:
logger.Verify(Log.With.LogLevel(LogLevel.Error)
.And.LogMessage("Process failed; requestId: '{requestId}', topic: '{topic}', status: '{status}'")
.And.LoggedValue("requestId", requestId.ToString())
.And.LoggedValue("topic", "Merge files")
.And.LoggedValue("status", "Failed")
.And.ExceptionMessage("Bar"),
Times.Once);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
944 次 |
| 最近记录: |