在没有构造函数注入的情况下使用 ILogger<T>

Using ILogger<T> without constructor injection

我的注册和构造函数设置如下,通过 Microsoft.Extensions.Logging 使用日志记录,

public MyService(ILogger<MyService> logger) {}

不过,我想做的是在 Startup.cs 中使用 services.AddSingleton() 以避免使用构造函数注入。原因是否则在测试服务时将需要修改所有单元测试以模拟记录器。
我相信有一种方法可以在 ServiceCollection 中注册 ILogger 的特定实例,但我无法整理出所需的语法。

谢谢!

  • 在单元测试中你不应该使用 DI 容器。
    • 如果是,那么您正在编写集成测试,而不是单元测试。
  • 在单元测试中,一般来说,测试的安排部分应该set-up你的SUT通过直接调用SUT的构造函数和passing-in test/mock/stub 其依赖项的版本。
  • 在单元测试中,理想情况下,您应该提供整个 testing-implementation 的整个 Microsoft.Extensions.Logging.ILoggerProvider 接口(以及相关的 类),这将允许您断言特定的日志输出消息已写入,或将 ILoggerILogger<T> 输出重定向到您的测试自己的输出。
  • 或者,如果您的测试不关心 ILogger 用法 and/or 如果您不想将 SUT 自己的日志输出写入测试输出,then use NullLoggerFactoryNullLogger<T>.Instance 在您的 安排 部分中创建一个不执行任何操作的存根 ILogger<T>

像这样:

using Microsoft.Extensions.Logging.Abstractions;

// [...]

[Fact]
public void MyService_should_do_something()
{
    // Arrange:

    IArbitraryDependency dep         = new StubArbitraryDependency();
#if FULL_MOON_TONIGHT
    ILogger<MyService>   nullLogger  = NullLoggerFactory.Instance.CreateLogger<MyService>();
#else
    ILogger<MyService>   nullLogger  = NullLogger<MyService>.Instance();
#endif

    MyService systemUnderTest = new MyService(
        arbitraryDependency: dep,
        logger             : nullLogger
    );

    // Act:

    systemUnderTest.DoSomething();

    // Assert:
    // [...]
}