Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Moq Mock a LoggerFactory in C# AspNet Core

Tags:

I am trying to write some unit tests for controller actions. To do that, I am using XUnit and Moq. The controllers have an ILoggerFactory injected in the constructor. How does one Moq this up for testing?

I have tried mocking a Logger for the controller class, and then mocking up CreateLogger to return the mock Logger, but I keep getting various test runtime NullReferenceExceptions when the LogInformation() function is called.

        //   Logger that yields only disappointment...                   var mockLogger = new Mock<ILogger<JwtController>>();         mockLogger.Setup(ml => ml.Log(It.IsAny<LogLevel>(), It.IsAny<EventId>(), It.IsAny<object>(), It.IsAny<Exception>(), It.IsAny<Func<object, Exception, string>>()));         var mockLoggerFactory = new Mock<ILoggerFactory>();         mockLoggerFactory.Setup(mlf => mlf.CreateLogger("JwtController")).Returns(mockLogger.Object); 

I assume the problem is that LogInformation is being called, and this is an extension method, so how to moq that?

like image 321
Steve Hibbert Avatar asked Jul 20 '17 17:07

Steve Hibbert


People also ask

How do you mock an ILogger?

To mock an ILogger<T> object, we can use Moq library to instantiate a new Mock<ILogger<T>>() object. Make sure you have installed the latest Moq package (v4. 15.1 as of Nov. 16, 2020, which contains an update to support “nested” type matchers).

What can be mocked with Moq?

You can use Moq to create mock objects that simulate or mimic a real object. Moq can be used to mock both classes and interfaces.

How does Moq mock work?

Mock objects allow you to mimic the behavior of classes and interfaces, letting the code in the test interact with them as if they were real. This isolates the code you're testing, ensuring that it works on its own and that no other code will make the tests fail.

What is Moq C#?

Moq is a mocking framework for C#/. NET. It is used in unit testing to isolate your class under test from its dependencies and ensure that the proper methods on the dependent objects are being called. For more information on mocking you may want to look at the Wikipedia article on Mock Objects.


2 Answers

For what it's worth: instead of mocking an ILoggerFactory, you could also pass an instance of NullLoggerFactory. This NullLoggerFactory will return instances of NullLogger. According to the docs, this is a:

Minimalistic logger that does nothing.

like image 53
Benjamin Avatar answered Sep 18 '22 12:09

Benjamin


I just mock the ILogger extension methods as below, and use a value function in the ILoggerFactory setup that returns the Mock ILogger object.

var mockLogger = new Mock<ILogger<[YOUR_CLASS_TYPE]>>(); mockLogger.Setup(     m => m.Log(         LogLevel.Information,         It.IsAny<EventId>(),         It.IsAny<object>(),         It.IsAny<Exception>(),         It.IsAny<Func<object, Exception, string>>()));  var mockLoggerFactory = new Mock<ILoggerFactory>(); mockLoggerFactory.Setup(x => x.CreateLogger(It.IsAny<string>())).Returns(() => mockLogger.Object); 

This will return your mocked Logger and you can verify any calls on it. No need to write wrappers or helpers.

You can even mock the IsEnabled, which is necessary for some code that leverages that functionality.

        mockLogger.Setup(             m => m.IsEnabled(                 Microsoft.Extensions.Logging.LogLevel.Debug)).Returns(true); 

Because there is only one method to mock, (and that you have to call), below shows the logging call you (might) use to have everything pointed to the this exact method.

 catch (ArgumentOutOfRangeException argEx) {     // this.logger.LogError(argEx, argEx.Message); /* << this is what you would like to do, BUT it is an extension method, NOT (easily) mockable */     Func<object, Exception, string> returnAnEmptyStringFunc = (a, b) => string.Empty;     this.logger.Log(LogLevel.Error, ushort.MaxValue, argEx.Message, argEx, returnAnEmptyStringFunc);     throw argEx; } 
like image 27
Ryannet Avatar answered Sep 18 '22 12:09

Ryannet