-
Couldn't load subscription status.
- Fork 57
Handling ILogger in your tests
ILogger and ILogger<T> are common services that you might use in your code or in code from other libraries you might be using. If you want to test code that has ILogger / ILogger<T> in them, then you need a way to provide the correct logger type to make your code work. There are two approaches to handling ILogger / ILogger<T>, depending on whether you need dependency injection (DI) in your tests.
The EfCore.TestSupport contains a MyLoggerProviderActionOut which is a ILoggerProvider. This logger provider will call an action every time a log is and returns a LogOutput class containing the various parts of the log it is linked to. Below is the code you need to turn the MyLoggerProviderActionOut into a ILogger or ILogger<T>.
NOTE: The MyLoggerProviderActionOut has a second parameter called logLevel, which defaults to LogLevel.Information. This means only logs with LogLevel.Information or higher will cause a call on the action. You can change the LogLevel by providing a different value.
Most logging uses the ILogger<T> type and here is you can MyLoggerProviderActionOut to create such a log, in this case a Ilogger<MyLoggerType>.
public List<LogOutput> Logs { get; } = new List<LogOutput>();
ILogger<MyLoggerType> logger = new LoggerFactory(
new[] { new MyLoggerProviderActionOut(log => Logs.Add(log)) })
.CreateLogger<MyLoggerType>();If you want a non-generic ILogger, then you use the code below:
public List<LogOutput> Logs { get; } = new List<LogOutput>();
ILogger logger = new LoggerFactory(
new[] { new MyLoggerProviderActionOut(Logs.Add) })
.CreateLogger("category name");With complex code with lots of setup its sometimes useful to setup the code used in your Program file. If any of the code uses a ILogger / ILogger<T>, then your setup of the services will fail with a exception saying "Unable to resolve service..." (see TestDependencyInjectionFailIfNoLogger test in the TestMyLoggerProviderActionOut class) . You have two options to handle logging in DI.
The simple approach is to use AddLogging, which means any code that uses a ILogger<T> service will work, but you won't see the log output.
var services = new ServiceCollection();
services.AddTransient<MyService>(); //service that uses logging
services.AddLogging();
//... other DI registering left out
var serviceProvider = services.BuildServiceProvider();NOTE: See the TestAddLoggerInDependencyInjection test method in the TestMyLoggerProviderActionOut class for a full test.
Sometimes you may want to inspect the logs coming from a specific service. You can do this by registering a singleton logger against the service's logger type. In the code below shows how to do this.
var logs = new List<LogOutput>();
var services = new ServiceCollection();
services.AddTransient<MyService>(); //service that uses logging
services.AddSingleton<ILogger<MyService>>(x =>
new LoggerFactory(
new[] { new MyLoggerProviderActionOut(l => logs.Add(l)) })
.CreateLogger<MyService>());
//... other DI registering left out
var serviceProvider = services.BuildServiceProvider();NOTE: See the TestAddingLoggerInDependencyInjection test method in the TestMyLoggerProviderActionOut class for a full test.
My tests says that you can have both services.AddLogging() and registered services.AddSingleton<ILogger... together - you will get the logging from the specified logging type and any other logging will be discarded. See the TestAddingLoggerInDependencyInjectionWithAddLogging test method in the TestMyLoggerProviderActionOut class
- Testing against a PostgreSQL db
- Changes in EfCore.TestSupport 5
- Testing with production data
- Using an in-memory database (old)