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

How to verify log message in Unit testing for a passing test?

我正在测试端点。我需要弄清楚如何让我的 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);


        }

以下是返回 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);

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

您可以使用表达式来匹配 logText 字符串

,而不是仅将 null 值传递给 LogInfo 方法的 Setup
loggerMock
    .Setup(x => x.LogInfo(It.Is<string>(s => s.Contains("Exception Report retrieved successfully."))))
    .Verifiable();

并在 Assert 步骤中使用 Verify()

loggerMock.Verify();

它确保 loggerMock 中的 LogInfo() 方法被调用,字符串匹配指定的表达式。查看 Moq wiki 中的 matching arguments 了解更多详情

如果您在 .NET 中使用 LogErrorLogDebugLogWarning 等方法,这些方法是扩展方法,现在的问题是您无法模拟扩展方法.所以你需要做的是模拟当你调用LogErrorLogDebugLogWarning等方法时实际调用的底层方法。

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

Log方法的定义如下

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

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

[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);
}

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

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

 // 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);