Search code examples
c#asp.net-corenunitilogger

Unable to Mock ILogger in Nunit


Net core and NUnit test case. I have my controller as below.

public class MyController : ControllerBase
    {
      public MyController(ILogger<MyController > logger)
        {
            this.Logger = logger ?? throw new ArgumentNullException(nameof(logger));
        }
    private readonly ILogger<MyController > Logger;

    public async Task<ActionResult> GetList()
    {
      this.Logger.LogInformation($"MyList: Some results came");
    }
 }

Then below my unit test case.

 public class MyControllerTests
 {
   private Mock<ILogger<MyController>> Logger = new Mock<ILogger<MyController>>();
   internal MyController myController;
   public MyControllerTests()
        {
            this.myController= new MyController(this.Logger.Object);
        }
    [Test]
        public async Task ListTest()
        {
           Logger.Verify(
               x => x.Log(
                   LogLevel.Information,
                   It.IsAny<EventId>(),
                   It.Is<It.IsAnyType>((o, t) => string.Equals("MyList: Some results came", o.ToString(), StringComparison.InvariantCultureIgnoreCase)),
                   It.IsAny<Exception>(),
                   (Func<It.IsAnyType, Exception, string>)It.IsAny<object>()),
         //rest of the code but i am getting error in above line
        }
 }

Above code gives error

Moq.MockException : ILogger.Log(LogLevel.Information, 0, ListTest : Successfully Fetched the Results, null, Func<FormattedLogValues, Exception, string>) invocation failed with mock behavior Strict. All invocations on the mock must have a corresponding setup.

Can someone help me to fix this. Any help would be greatly appreciated. Thanks


Solution

  • You're very close. Here's an article about this. Here are the two working verification's I've come up with:

            _logTest.Process();
            _loggerMock.Verify(l => l.Log(
                LogLevel.Information,
                It.IsAny<EventId>(),
                It.IsAny<It.IsAnyType>(),
                It.IsAny<Exception>(),
                (Func<It.IsAnyType, Exception, string>)It.IsAny<object>()), Times.Exactly(1));
    

    This version allows you to get more specific:

            _loggerMock.Verify
            (
                l => l.Log
                (
                    //Check the severity level
                    LogLevel.Error,
                    //This may or may not be relevant to your scenario
                    It.IsAny<EventId>(),
                    //This is the magical Moq code that exposes internal log processing from the extension methods
                    It.Is<It.IsAnyType>((state, t) =>
                        //This confirms that the correct log message was sent to the logger. {OriginalFormat} should match the value passed to the logger
                        //Note: messages should be retrieved from a service that will probably store the strings in a resource file
                        CheckValue(state, "MyList: Some results came", "{OriginalFormat}") &&
                        //This confirms that an argument with a key of "recordId" was sent with the correct value
                        //In Application Insights, this will turn up in Custom Dimensions
                        CheckValue(state, recordId, nameof(recordId))
                ),
                //Confirm the exception type
                It.IsAny<ArgumentNullException>(),
                //Accept any valid Func here. The Func is specified by the extension methods
                (Func<It.IsAnyType, Exception, string>)It.IsAny<object>()),
                //Make sure the message was logged the correct number of times
                Times.Exactly(1)
            );