This is my controller:
public class BlogController : Controller
{
private IDAO _blogDAO;
private readonly ILogger _
Already mentioned you can mock it as any other interface.
var logger = new Mock<ILogger<QueuedHostedService>>();
So far so good.
Nice thing is that you can use Moq
to verify that certain calls have been performed. For instance here I check that the log has been called with a particular Exception
.
logger.Verify(m => m.Log(It.Is<LogLevel>(l => l == LogLevel.Information), 0,
It.IsAny<object>(), It.IsAny<TaskCanceledException>(), It.IsAny<Func<object, Exception, string>>()));
When using Verify
the point is to do it against the real Log
method from the ILooger
interface and not the extension methods.
For .net core 3 answers that are using Moq
https://stackoverflow.com/a/56728528/2164198
are no longer working due to a change described in the issue TState in ILogger.Log used to be object, now FormattedLogValues
Luckily stakx provided a nice workaround. So I'm posting it in hope it can save time for others (it took a while to figure the things out):
loggerMock.Verify(
x => x.Log(
LogLevel.Information,
It.IsAny<EventId>(),
It.Is<It.IsAnyType>((o, t) => string.Equals("Index page say hello", o.ToString(), StringComparison.InvariantCultureIgnoreCase)),
It.IsAny<Exception>(),
(Func<It.IsAnyType, Exception, string>) It.IsAny<object>()),
Times.Once);
Adding my 2 cents, This is a helper extension method typically put in a static helper class:
static class MockHelper
{
public static ISetup<ILogger<T>> MockLog<T>(this Mock<ILogger<T>> logger, LogLevel level)
{
return logger.Setup(x => x.Log(level, It.IsAny<EventId>(), It.IsAny<object>(), It.IsAny<Exception>(), It.IsAny<Func<object, Exception, string>>()));
}
private static Expression<Action<ILogger<T>>> Verify<T>(LogLevel level)
{
return x => x.Log(level, 0, It.IsAny<object>(), It.IsAny<Exception>(), It.IsAny<Func<object, Exception, string>>());
}
public static void Verify<T>(this Mock<ILogger<T>> mock, LogLevel level, Times times)
{
mock.Verify(Verify<T>(level), times);
}
}
Then, you use it like this:
//Arrange
var logger = new Mock<ILogger<YourClass>>();
logger.MockLog(LogLevel.Warning)
//Act
//Assert
logger.Verify(LogLevel.Warning, Times.Once());
And of course you can easily extend it to mock any expectation (i.e. expection, message, etc …)
If a still actual. Simple way do log to output in tests for .net core >= 3
[Fact]
public void SomeTest()
{
using var logFactory = LoggerFactory.Create(builder => builder.AddConsole());
var logger = logFactory.CreateLogger<AccountController>();
var controller = new SomeController(logger);
var result = controller.SomeActionAsync(new Dto{ ... }).GetAwaiter().GetResult();
}
And when using StructureMap / Lamar:
var c = new Container(_ =>
{
_.For(typeof(ILogger<>)).Use(typeof(NullLogger<>));
});
Docs:
Use a custom logger that uses ITestOutputHelper
(from xunit) to capture output and logs. The following is a small sample that only writes the state
to the output.
public class XunitLogger<T> : ILogger<T>, IDisposable
{
private ITestOutputHelper _output;
public XunitLogger(ITestOutputHelper output)
{
_output = output;
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
_output.WriteLine(state.ToString());
}
public bool IsEnabled(LogLevel logLevel)
{
return true;
}
public IDisposable BeginScope<TState>(TState state)
{
return this;
}
public void Dispose()
{
}
}
Use it in your unittests like
public class BlogControllerTest
{
private XunitLogger<BlogController> _logger;
public BlogControllerTest(ITestOutputHelper output){
_logger = new XunitLogger<BlogController>(output);
}
[Fact]
public void Index_ReturnAViewResult_WithAListOfBlog()
{
var mockRepo = new Mock<IDAO<Blog>>();
mockRepo.Setup(repo => repo.GetMany(null)).Returns(GetListBlog());
var controller = new BlogController(_logger,mockRepo.Object);
// rest
}
}