概要
UnitTest、書いてますか?
GenericHostやWebHostを使っている場合、テストコードを書くためには、必ずILogger<T>
のモックが必要になると思います。これを手軽に作成する方法を書きます。
最初に結論まとめ
こんな感じの共通クラスでCreateLogger<T>
メソッドを用意しておいて呼び出すことで、コンソール出力するILoggerを簡単に作れます。あとはこれを、コンストラクタ等のDI部分へ渡せばOKです。
internal class ILoggerToConsole
{
private static readonly ILoggerFactory _factory = LoggerFactory.Create(builder =>
{
builder
.AddConsole()
.SetMinimumLevel(LogLevel.Trace);
});
public static ILogger<T> CreateLogger<T>()
{
return _factory.CreateLogger<T>();
}
}
説明
そろそろ、GenericHostを使う人も増えてきたでしょうか。ASP.NET系のWebアプリならばWebHostをたぶん最初から使っていますね。それらでUnitTestを書こうとすると、最初に悩みどころになるのは、「ILogger<T>
に何を渡そうか」ではないでしょうか。
Moqを使っているならば、ILoggerのインターフェースを満たすモックを作れば良い。それはその通りです。しかし、メソッドも多いし自力で書くのはちょっと面倒です。
NUnitの場合、ILoggerの内容をどこへ出力するモックを書きたいかというと、たぶんコンソール出力になると思います。
それならば、コンソール出力をする本物のロガーを作成してしまった方が、実のところモック作成よりも手軽です。その方法を書きます。
まず、どこへ何を出力するロガーを作るのかを決めて、ILoggerFactoryを作成します。
var loggerFactory = LoggerFactory.Create(builder =>
{
builder
.AddConsole()
.SetMinimumLevel(LogLevel.Trace);
});
こんな感じです。ここでは、「コンソールに出力する」「出力対象の最小レベルをTraceとする」という設定です。
必要なのはILogger<T>
で、Tは対象のクラスによって変わりますね。それに合わせ、次のようにインスタンスを作成します。作成したら、対象のクラスへ渡せばOKです。
var logger = loggerFactory.CreateLogger<クラス名>();
クラスごと使い方を例示すると、こんな感じです。
class TestTarget(ILogger<TestTarget> _logger){
}
var loggerFactory = LoggerFactory.Create(builder =>
{
builder
.AddConsole()
.SetMinimumLevel(LogLevel.Trace);
});
var logger = loggerFactory.CreateLogger<TestTarget>();
var instance = new TestTarget(logger);
はい、これだけです!これだけで、コンソールへのログ出力をしてくれるILogger<T>
のモックのできあがりです。(モックというか、本物ですが)
LoggerFactoryは使い回せるので、次のような共通化したクラスを作っておいて、必要な時にCreateLogger<T>
を呼び出すと、使いやすいと思います。
internal static class ILoggerToConsole
{
private static readonly ILoggerFactory _factory = LoggerFactory.Create(builder =>
{
builder
.AddConsole()
.SetMinimumLevel(LogLevel.Trace);
});
public static ILogger<T> CreateLogger<T>()
{
return _factory.CreateLogger<T>();
}
}
さっきと同じようにクラスでの使い方を例示すると、こんな感じです。
class TestTarget(ILogger<TestTarget> _logger){
}
var logger = ILoggerToConsole.CreateLogger<TestTarget>();
var instance = new TestTarget(logger);
ちなみにコンソール出力がいらない場合は
コンソール出力するロガーを作って渡す方法を紹介しましたが、コンソール出力も不要な場合は実はもっと簡単な「NullLogger」という方法があります。そういう組み込みの型があるので、newするだけで使えます。
こんな使い方になります。
class TestTarget(ILogger<TestTarget> _logger){
}
var logger = new NullLogger<TestTarget>();
var instance = new TestTarget(logger);
まとめ
UnitTestのコードを頑張って書こうとしても、最初に「ロガーのインスタンスはどうしようか」で引っかかって挫折してしまってはもったいない。そういう問題はこの記事の定型コードで解決して、どんどんUnitTestのコードを書いていきましょう!