这是我的控制器:

public class BlogController : Controller
{
    private IDAO<Blog> _blogDAO;
    private readonly ILogger<BlogController> _logger;

    public BlogController(ILogger<BlogController> logger, IDAO<Blog> blogDAO)
    {
        this._blogDAO = blogDAO;
        this._logger = logger;
    }
    public IActionResult Index()
    {
        var blogs = this._blogDAO.GetMany();
        this._logger.LogInformation("Index page say hello", new object[0]);
        return View(blogs);
    }
}

正如你所看到的,我有两个依赖项,一个IDAO和一个ILogger

这是我的测试类,我使用xUnit来测试和Moq来创建模拟和存根,我可以很容易地模拟DAO,但使用ILogger,我不知道该做什么,所以我只是传递null并注释掉运行测试时登录控制器的调用。是否有一种方法可以测试,但仍然以某种方式保存日志?

public class BlogControllerTest
{
    [Fact]
    public void Index_ReturnAViewResult_WithAListOfBlog()
    {
        var mockRepo = new Mock<IDAO<Blog>>();
        mockRepo.Setup(repo => repo.GetMany(null)).Returns(GetListBlog());
        var controller = new BlogController(null,mockRepo.Object);

        var result = controller.Index();

        var viewResult = Assert.IsType<ViewResult>(result);
        var model = Assert.IsAssignableFrom<IEnumerable<Blog>>(viewResult.ViewData.Model);
        Assert.Equal(2, model.Count());
    }
}

当前回答

最简单的解决方案是使用NullLogger。它是Microsoft.Extensions.Logging.Abstractions的一部分。

没有必要干扰工厂和其他不必要的建设。添加:

ILogger<BlogController> logger = new NullLogger<BlogController>();

其他回答

更新(感谢@Gopal Krishnan的评论):

随着Moq >= 4.15.0,以下代码正在工作(不再需要强制转换):

 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>(),
                    It.IsAny<Func<It.IsAnyType, Exception?, string>>()),
                Times.Once);

之前版本的答案(Moq < 4.15.0):

对于使用Moq的。net core 3答案

https://stackoverflow.com/a/54646657/2164198 https://stackoverflow.com/a/54809607/2164198 https://stackoverflow.com/a/56728528/2164198

由于在ILogger.Log中的TState曾经是对象,现在是FormattedLogValues问题中描述的变化,不再工作

幸运的是,stakx提供了一个很好的解决方案。所以我把它贴出来,希望能帮别人节省时间(花了一段时间才弄明白):

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

当使用StructureMap / Lamar时:

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

文档:

https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging.abstractions.nulllogger?view=aspnetcore-2.1 http://structuremap.github.io/generics/

使用一个使用ITestOutputHelper(来自xunit)的自定义记录器来捕获输出和日志。下面是一个只将状态写入输出的小示例。

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()
    {
    }
}

在单元测试中使用它

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
  }
}

使用Telerik Just Mock创建记录器的模拟实例:

using Telerik.JustMock;
...
context = new XDbContext(Mock.Create<ILogger<XDbContext>>());

实际上,我找到了Microsoft.Extensions.Logging.Abstractions。NullLogger<>,这看起来像是一个完美的解决方案。安装包Microsoft.Extensions.Logging。抽象,然后按照示例配置和使用它:

using Microsoft.Extensions.Logging;

public void ConfigureServices(IServiceCollection services)
{
    ...

    services.AddSingleton<ILoggerFactory, NullLoggerFactory>();

    ...
}
using Microsoft.Extensions.Logging;

public class MyClass : IMyClass
{
    public const string ErrorMessageILoggerFactoryIsNull = "ILoggerFactory is null";

    private readonly ILogger<MyClass> logger;

    public MyClass(ILoggerFactory loggerFactory)
    {
        if (null == loggerFactory)
        {
            throw new ArgumentNullException(ErrorMessageILoggerFactoryIsNull, (Exception)null);
        }

        this.logger = loggerFactory.CreateLogger<MyClass>();
    }
}

单元测试

//using Microsoft.VisualStudio.TestTools.UnitTesting;
//using Microsoft.Extensions.Logging;

[TestMethod]
public void SampleTest()
{
    ILoggerFactory doesntDoMuch = new Microsoft.Extensions.Logging.Abstractions.NullLoggerFactory();
    IMyClass testItem = new MyClass(doesntDoMuch);
    Assert.IsNotNull(testItem);
}