コンストラクターシグネチャを持つクラスが与えられた
public Foo(ILogger<Foo> logger) {
// ...
}
テストしたいのですが、テストでILogger<Foo>
を提供する方法が必要です。 以前に尋ねられた ですが、 そのときの唯一の答え は、本格的なサービスレジストリを設定し、ロギングを構成し、そこからロガーを解決することでした。これは私には非常にやり過ぎのようです。
テスト目的でILogger<T>
の実装を提供する簡単な方法はありますか?
注:実際に何かを記録する必要はありません。テスト対象の被験者が何かを記録しようとしたときに爆発することはありません。
2つのオプションがあります。
ILogger<Foo>
の空の実装を手動で作成し、そのインスタンスをctorに渡します。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);
}
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>();
ILogger<T>
のドキュメント (私の強調)から:
カテゴリ名が指定されたTCategoryNameタイプ名から派生するロギング用の汎用インターフェース。 一般に、依存性注入から名前付き ILogger のアクティブ化を有効にするために使用されます。
したがって、1つのオプションは、Foo
メソッドの実装を変更してプレーンな ILogger
を取得し、 NullLogger
を使用することです。 =実装。
インスタンスを提供するだけでなく、呼び出しを検証する必要がある場合は、多少複雑になります。その理由は、ほとんどの呼び出しが実際には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");
これは私のために働いた:
private FooController _fooController;
private Mock<ILogger<FooController>> _logger;
[TestInitialize]
public void Setup()
{
_logger = new Mock<ILogger<FooController>>();
_fooController = new FooController(_logger.Object);
}
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);
});