NSubstitute.Community.Logging is a collection of helper methods that make it easy to inject substitute loggers into an IServiceProvider and verify that specific logging activity occurred.
First install NSubstitute.Community.Logging into your test project from Nuget.
Just call .AddSubstituteLoggers()
on your ServiceCollection before buiding it into an IServiceProvider to add ILoggerFactory, ILogger, and ILogger generated using NSubstitute.
var serviceProvider = new ServiceCollection().AddSubstituteLoggers().BuildServiceProvider();
var mockLoggerFactory = serviceProvider.GetService<ILoggerFactory>();
var mockLogger = serviceProvider.GetService<ILogger>();
var mockLoggerT = serviceProvider.GetService<ILogger<MyClass>>();
NSubstitute.Community.Logging lets you easily assert that specific logging took place. All you need is any NSubstitute substitute ILogger. Then you can verify logs using syntax similar to how the application wrote them. See LoggerExtensionsTests for more examples.
var Target = Substitute.For<ILogger>();
var exception = new KeyNotFoundException();
var message = "This is a {log} message";
var args = new object[] { "log" };
Microsoft.Extensions.Logging.LoggerExtensions.LogInformation(Target, exception, message, args);
Target.Received(1)
.LogInformation(exception, message, args);
Target.DidNotReceive()
.LogInformation(exception, message);
Target.DidNotReceive()
.LogInformation(message, args);
Target.DidNotReceive()
.LogInformation(message);
Sometimes you don't want to or can't match on every nuance of a log message. For these cases there is CallToLog
which behaves like Arg.Is
for any provided argument and behaves like Arg.Any
for omitted arguments. This allows you to verify log events by:
- log level
- OriginalFormat
- event Id
- exception
- any combination of the above in the same log!
NOTE: CallToLog
methods work with any NSubstitute Recieved Calls
var Target = Substitute.For<ILogger>();
Microsoft.Extensions.Logging.LoggerExtensions.LogInformation(Target, "I am an informational log.");
Target.DidNotReceive()
.CallToLog(LogLevel.Warning);
Target.DidNotReceive()
.CallToLog(LogLevel.Error);
var Target = Substitute.For<ILogger>();
Microsoft.Extensions.Logging.LoggerExtensions.LogInformation(Target, "I am an informational log.");
Target.Received(1)
.CallToLog(LogLevel.Information, "I am an informational log.");
Somes you want to verify the value of arguments provided to a Log but cannot match all the arguments or cannot match the exact value that was used. See CallToLogPredicateTests for more examples.
var Target = Substitute.For<ILogger>();
Microsoft.Extensions.Logging.LoggerExtensions.LogWarning(Target, "{ErrorCode} There were {ErrorCount} errors that happened on {Where}.", Guid.NewGuid(), 13, "earth");
Target.Received(1)
.CallToLog(LogLevel.Warning,
_ => _.OriginalFormat.Equals("{ErrorCode} There were {ErrorCount} errors that happened on {Where}.")
&& _.KeyEquals("ErrorCount", 13)
&& _.KeyEquals("Where", "earth")
);
var Target = Substitute.For<ILogger>();
var now = DateTime.Now;
Microsoft.Extensions.Logging.LoggerExtensions.LogWarning(Target, "there were {oopies} things you might want to know about. {where} {when}", 13, "earth", now);
var planets = new[] { "mars", "earth" };
Target.Received(1)
.CallToLog(LogLevel.Warning,
_ => _.OriginalFormat.StartsWith("there were {oopies}")
&& _.TryGetValue("oopies", out int oopies) && oopies > 10
&& _.TryGetValue("where", out string where) && planets.Contains(where)
&& _.TryGetValue("when", out DateTime when) && when <= DateTime.Now
);
See the tests for more examples!!!