0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

xUnit の単体テストで ILogger への出力を実現する

Posted at

やりたいこと

  • xUnit による単体テストの中で ILogger<T> が注入されるクラスをテストする
  • 記録される内容を確認できるようにする

環境

  • Microsoft Visual Studio Community 2022 Version 17.9.7
  • .NET8 C# 12

テスト対象

MessageService.cs
using Microsoft.Extensions.Logging;

namespace HigeDaruma.DemoXUnit.ClassLibrary.Messages;

public sealed class MessageService(ILogger<MessageService> logger)
{
    public string GetMessage(string name)
    {
        logger.LogInformation("Method:{Method},Name:{Name}", "MessageService.GetMessage(string)", name);

        return $"Hello, World {name} !";
    }
}

プライマリーコンストラクターに注入された ILogger<T> を使ってロギングしています。このクラスをテストできるようにし、LogInformation の内容を確認できるようにします。

手順

まずは ITestOutputHelper をつかってみる

xUnit で単体テストを実装した場合、テストエクスプローラーにログを表示するには ITestOutputHelper を活用します。

MessageServiceTest.cs
using Xunit.Abstractions;

namespace HigeDaruma.DemoXUnit.ClassLibrary.UnitTest.Messages;

public sealed class MessageServiceTest
{
    private readonly ITestOutputHelper _testOutputHelper;

    public MessageServiceTest(ITestOutputHelper testOutputHelper)
    {
        _testOutputHelper = testOutputHelper;
    }

    [Fact]
    public void TestOutputHelperTest()
    {
        string name = "HigeDaruma";

        _testOutputHelper.WriteLine(name);
    }
}

個のテストを実行すると、テストエクスプローラーの「テスト詳細の概要」(どっちやねん)の「標準出力」の領域に WriteLine() した内容が表示されています。

image.png

この ITestOutputHelperILogger<T> として注入できるようにすれば、やりたいことを実現できそうです。

xUnit プロジェクトに NuGet パッケージを追加する

  1. Microsoft.Extensions.Logging を追加します

Logger/LoggerProvider を実装する

  1. ILogger を実装する XunitLogger を追加します
XunitLogger.cs
using Microsoft.Extensions.Logging;
using Xunit.Abstractions;

namespace HigeDaruma.DemoXUnit.ClassLibrary.UnitTest;

internal sealed class XunitLogger(
    ITestOutputHelper testOutputHelper,
    string categoryName) : ILogger
{
    public IDisposable? BeginScope<TState>(TState state) where TState : notnull
    {
        return NopDisposable.Instance;
    }

    public bool IsEnabled(LogLevel logLevel)
    {
        return true;
    }

    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
    {
        testOutputHelper.WriteLine(($"{categoryName} [{eventId}] {formatter(state, exception)}");

        if (exception is not null)
        {
            testOutputHelper.WriteLine(exception.ToString());
        }
    }

    private class NopDisposable : IDisposable
    {
        public static NopDisposable Instance = new();
        public void Dispose()
        { }
    }
}

2. ILoggerProvider を実装する XunitLoggerProvider を追加する

XunitLoggerProvider.cs
using Microsoft.Extensions.Logging;
using Xunit.Abstractions;

namespace HigeDaruma.DemoXUnit.ClassLibrary.UnitTest;

internal sealed class XunitLoggerProvider(
    ITestOutputHelper testOutputHelper) : ILoggerProvider
{
    public ILogger CreateLogger(string categoryName)
    {
        return new XunitLogger(testOutputHelper, categoryName);
    }

    public void Dispose()
    {
    }
}

3. XunitLoggerILogger<T> として注入できるように準備

MessageServiceTest.cs
using Xunit.Abstractions;

namespace HigeDaruma.DemoXUnit.ClassLibrary.UnitTest.Messages;

public sealed class MessageServiceTest
{
    private readonly ITestOutputHelper _testOutputHelper;
+
+    private readonly ILogger<MessageService> _logger;

    public MessageServiceTest(ITestOutputHelper testOutputHelper)
    {
        _testOutputHelper = testOutputHelper;
+
+        LoggerFactory loggerFactory = new();
+        loggerFactory.AddProvider(new XunitLoggerProvider(testOutputHelper));
+        _logger = loggerFactory.CreateLogger<MessageService>();
    }

    [Fact]
    public void TestOutputHelperTest()
    {
        string name = "HigeDaruma";

        _testOutputHelper.WriteLine(name);
    }
}

4. テストメソッドで使用する

MessageSAerviceTest.cs
    [Fact]
    public void GetMessage_ReturnsOk_WhenInputNameIsOk()
    {
        // Arrange
        string name = "HigeDaruma";
        MessageService instance = new(_logger);

        // Act
        string result = instance.GetMessage(name);

        // Assert
        Assert.Equal("Hello, World HigeDaruma !", result);
    }

MessageService のコンストラクターに _logger を注入し、GetMessage(string) を実行します。インスタンスを生成できるだけではなく、テストエクスプローラーにもログ内容が表示されます。

image.png

まとめ

xUnit でデバッグに使える ITestOutputHelper を使って ILogger<T> を実現しました。これで単体テストがはかどります。

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?