web-dev-qa-db-ja.com

.NETCoreコードの単体テストでILogger <T>を提供するにはどうすればよいですか?

コンストラクターシグネチャを持つクラスが与えられた

public Foo(ILogger<Foo> logger) {
    // ...
}

テストしたいのですが、テストでILogger<Foo>を提供する方法が必要です。 以前に尋ねられた ですが、 そのときの唯一の答え は、本格的なサービスレジストリを設定し、ロギングを構成し、そこからロガーを解決することでした。これは私には非常にやり過ぎのようです。

テスト目的でILogger<T>の実装を提供する簡単な方法はありますか?

注:実際に何かを記録する必要はありません。テスト対象の被験者が何かを記録しようとしたときに爆発することはありません。

10
Tomas Aschan

2つのオプションがあります。

  1. ILogger<Foo>の空の実装を手動で作成し、そのインスタンスをctorに渡します。
  2. Moq、NSubstituteなどのモックフレームワークを使用して、同じ空の実装をその場で作成します。

NullLoggerFactory をファクトリとして使用して、ILogger<Foo>のインスタンスを作成できます。

次のコントローラーについて考えてみます。

public abstract class Foo: Controller
{
    public Foo(ILogger<Foo> logger) {
        Logger = logger;
    }

    public ILogger Logger { get; private set; }
}

サンプルの単体テストは次のようになります。

[TestMethod]
public void FooConstructorUnitTest()
{
    // Arrange
    ILogger<FooController> logger = new Logger<FooController>(new NullLoggerFactory());

    // Act
    FooController target = new FooController(logger);

    // Assert
    Assert.AreSame(logger, target.Logger);
}
11
Adrian Toman

Dotnet core 2.0以降、一般的なNullLogger <T>クラスが利用可能になりました。

var foo = new Foo(NullLogger<Foo>.Instance);

https://docs.Microsoft.com/en-us/dotnet/api/Microsoft.extensions.logging.abstractions.nulllogger-1?view=aspnetcore-2.1 (docs) https ://github.com/aspnet/Logging/blob/master/src/Microsoft.Extensions.Logging.Abstractions/NullLoggerOfT.cs (ソース)

または、サービスの一部として必要な場合:

services.AddSingleton<ILoggerFactory, NullLoggerFactory>();

https://docs.Microsoft.com/en-us/dotnet/api/Microsoft.extensions.logging.abstractions.nullloggerfactory?view=aspnetcore-2.1 (docs)

8

ILogger<T>のドキュメント (私の強調)から:

カテゴリ名が指定されたTCategoryNameタイプ名から派生するロギング用の汎用インターフェース。 一般に、依存性注入から名前付き ILogger のアクティブ化を有効にするために使用されます。

したがって、1つのオプションは、Fooメソッドの実装を変更してプレーンな ILogger を取得し、 NullLogger を使用することです。 =実装。

1
DavidG

インスタンスを提供するだけでなく、呼び出しを検証する必要がある場合は、多少複雑になります。その理由は、ほとんどの呼び出しが実際にはILoggerインターフェース自体に属していないためです。

私はより詳細な答えを書きました ここ

ここに小さな概要があります。

NSubstituteで動作するように作成したメソッドの例:

public static class LoggerTestingExtensions
{
    public static void LogError(this ILogger logger, string message)
    {
        logger.Log(
            LogLevel.Error,
            0,
            Arg.Is<FormattedLogValues>(v => v.ToString() == message),
            Arg.Any<Exception>(),
            Arg.Any<Func<object, Exception, string>>());
    }

}

そして、これはそれがどのように使われることができるかです:

_logger.Received(1).LogError("Something bad happened");   
0

これは私のために働いた:

private FooController _fooController;
private Mock<ILogger<FooController>> _logger;

[TestInitialize]
public void Setup()
{
    _logger = new Mock<ILogger<FooController>>();
    _fooController = new FooController(_logger.Object);
}
0
DanielV

ILoggerをあざけるためにこれを試してみてください:

mock.Setup(m => m.Log<object>(It.IsAny<LogLevel>(),It.IsAny<EventId>(),It.IsAny<object>(),It.IsAny<Exception>(),It.IsAny<Func<object, Exception,string>>()))
    .Callback<LogLevel, EventId, object, Exception, Func<object, Exception, string>>((logLevel, eventId, obj, exception, func) => 
    {
        string msg = func.Invoke(obj, exception);
        Console.WriteLine(msg);
    });
0
Jean Sena