如何验证单元测试中的日志消息以通过测试?

JCA*_*JCA 6 c# unit-testing moq xunit asp.net-core

我正在测试一个端点。我需要弄清楚如何让我的 loggerMock 测试通过。以下是我目前设置测试的方式:

public void GetExceptionReportSessionData_Returns200OK()
        {
            //Arrange 
            var response = new RetrieveExceptionReportSessionDatesResponse
            {
                RetrieveExceptionReportSessionDatesResult = string.Empty
            };
            var serviceClient = new Mock<WorkflowService.WorkflowService>();      
            serviceClient
                .Setup(x => x.RetrieveExceptionReportSessionDatesAsync(It.IsAny<RetrieveExceptionReportSessionDatesRequest>()))
               .ReturnsAsync(response);

            var loggerMock = new Mock<ILogger>();
            loggerMock.Setup(x => x.LogInfo(null));

            var controller = new ExceptionReportController(loggerMock.Object);

            var ctx = new ControllerContext() { HttpContext = new DefaultHttpContext() };
            ctx.HttpContext.Request.Headers["token"] = "fake_token_here"; //Set header
            controller.ControllerContext = ctx;

            //Act
            var result = controller.GetExceptionReportSessionData();

            //Assert
            var viewResult = Assert.IsType<OkObjectResult>(result);
            Assert.Equal(StatusCodes.Status200OK, viewResult.StatusCode);


        }
Run Code Online (Sandbox Code Playgroud)

以下是返回 200 时记录器在端点中的设置方式:

if (result != null && result.ExceptionReportLines != null && result.ExceptionReportLines.Count > 0)
            {
                logText = LogFormatter.Format(
                                WebUtilities.GetUser((ClaimsIdentity)HttpContext.User.Identity),
                                startTime, DateTime.Now, Privilege.ViewOrderExceptionReport,
                                "Get Exception Report", "Exception Report retrieved successfully.");
                logger.LogInfo(logText);
            }
            else
            {
                logText = LogFormatter.Format
                                (WebUtilities.GetUser((ClaimsIdentity)HttpContext.User.Identity),
                                startTime, DateTime.Now, Privilege.ViewOrderExceptionReport,
                                "Get Exception Report", "Exception report is empty for the given report filters.");
                logger.LogWarn(logText);
            }

            return Ok(result);
Run Code Online (Sandbox Code Playgroud)

我的测试已设置为显示后一条消息。我怎样才能通过测试?

Nir*_*edi 17

如果您在 .NET 中使用LogErrorLogDebugLogWarning方法,这些方法就是扩展方法,现在的问题是您无法模拟扩展方法。LogError因此,您需要做的是模拟当您调用、LogDebug等方法时实际调用的底层方法LogWarning

实际上,所有这些方法都调用Log方法,因此您需要模拟Log方法。

方法定义Log如下

  void Log (this ILogger logger, LogLevel logLevel, EventId eventId, Exception exception, string message, params object[] args)
Run Code Online (Sandbox Code Playgroud)

您可以使用扩展方法Log 来模拟方法,LogWarning如下所示

[Fact]
public void VerifyLogWarning()
{
    // Arrange
    var loggerMock = new Mock<ILogger<MyClass>>();
    var myclass = new MyClass(loggerMock.Object);

    // Act
    myclass.TestMethod();

    // Assert
    loggerMock.Verify(
        x => x.Log(
            It.Is<LogLevel>(l => l == LogLevel.Warning),
            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)

在上面的代码中,我们正在验证是否已调用LogWarning一次作为调用的一部分TestMethod()

您还可以验证日志警告消息,如下所示

 // Assert
    loggerMock.Verify(
        x => x.Log(
            It.Is<LogLevel>(l => l == LogLevel.Warning),
            It.IsAny<EventId>(),
            It.Is<It.IsAnyType>((v, t) =>  v.ToString() == "LogWarning Message......"),
            It.IsAny<Exception>(),
            It.Is<Func<It.IsAnyType, Exception, string>>((v, t) => true)), Times.Once);
Run Code Online (Sandbox Code Playgroud)


Pav*_*ski 3

您可以使用表达式来匹配字符串,而不是仅nullSetupof方法传递值LogInfologText

loggerMock
    .Setup(x => x.LogInfo(It.Is<string>(s => s.Contains("Exception Report retrieved successfully."))))
    .Verifiable();
Run Code Online (Sandbox Code Playgroud)

Verify()并在Assert步骤中使用

loggerMock.Verify();
Run Code Online (Sandbox Code Playgroud)

它确保使用字符串调用的LogInfo()方法loggerMock与指定的表达式匹配。查看wiki中的匹配参数Moq以获取更多详细信息

  • ILogger 没有 LogInfo 方法,它有 LogInformation 方法,并且它是静态扩展方法,因此不能与 Moq 一起使用。我不确定这是如何运作的。 (3认同)