JCA
JCA

Reputation: 287

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

I'm testing an endpoint. I need to figure out how to get the test to pass with my loggerMock. Here is how I currently have the test set up:

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


        }

Here is how the logger is setup in the endpoint when returning a 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);

My test is set up so that the latter message appears. How can I get the test to pass?

Upvotes: 10

Views: 23780

Answers (2)

Niraj Trivedi
Niraj Trivedi

Reputation: 2880

If you are using LogError, LogDebug, LogWarning etc methods in .NET those methods are extension methods and now the issue is that you can't mock an extension method. So what you need to do is mock the underlying method which is actually called when you call LogError, LogDebug, LogWarning etc methods.

Actually, all of those methods calls Log method so you need to mock Log method.

Definition of Log method as below

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

You can mock Log method with LogWarning extension method as below

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

In the above code, we are verifying whether we have called LogWarning once as a part of TestMethod() call.

You can also verify log warning message as below

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

Upvotes: 25

Pavel Anikhouski
Pavel Anikhouski

Reputation: 23228

Instead of passing just a null value to Setup of LogInfo method you can use an expression to match the logText string

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

And use Verify() in Assert step

loggerMock.Verify();

It ensures, that LogInfo() method in loggerMock was called with string matches the specified expression. Have a look at matching arguments in Moq wiki for more details

Upvotes: 3

Related Questions