-
Notifications
You must be signed in to change notification settings - Fork 56
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)