How to unit test with ILogger in ASP.NET Core

后端 未结 14 1190
南旧
南旧 2020-12-04 11:47

This is my controller:

public class BlogController : Controller
{
    private IDAO _blogDAO;
    private readonly ILogger _         


        
相关标签:
14条回答
  • 2020-12-04 12:14

    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.

    0 讨论(0)
  • 2020-12-04 12:17

    For .net core 3 answers that are using Moq

    • https://stackoverflow.com/a/54646657/2164198
    • https://stackoverflow.com/a/54809607/2164198
    • 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);
    
    0 讨论(0)
  • 2020-12-04 12:17

    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 …)

    0 讨论(0)
  • 2020-12-04 12:19

    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();
    }
    
    0 讨论(0)
  • 2020-12-04 12:21

    And when using StructureMap / Lamar:

    var c = new Container(_ =>
    {
        _.For(typeof(ILogger<>)).Use(typeof(NullLogger<>));
    });
    

    Docs:

    • https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging.abstractions.nulllogger?view=aspnetcore-2.1
    • http://structuremap.github.io/generics/
    0 讨论(0)
  • 2020-12-04 12:22

    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
      }
    }
    
    0 讨论(0)
提交回复
热议问题