cover

If you are running integration or component tests with xUnit or other runner, having logs in the test console is helpful for:

Quick guide

First implement define the XUnitLogger class that implements build in ILogger class.

internal class XunitLogger : ILogger
{
    private readonly ITestOutputHelper _output;
    private readonly LogLevel _minLogLevel;
    private readonly string _categoryName;

    public XunitLogger(ITestOutputHelper output, LogLevel minLogLevel, string categoryName)
    {
        _output = output;
        _minLogLevel = minLogLevel;
        _categoryName = categoryName;
    }

    public IDisposable? BeginScope<TState>(TState state) where TState : notnull
    {
        throw new NotImplementedException();
    }

    public bool IsEnabled(LogLevel logLevel) => logLevel >= _minLogLevel;

    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
    {
        if (!IsEnabled(logLevel))
            return;
        _output.WriteLine($"{logLevel} - {_categoryName} - {formatter(state, exception)}");
    }
}

ILoggerFactory Class: Create a factory for generating ILogger instances, tailored for xUnit.

class XUnitLoggingFactory : ILoggerFactory
{
    private readonly ITestOutputHelper _output;
    private readonly LogLevel _minLogLevel;

    public XUnitLoggingFactory(ITestOutputHelper output, LogLevel minLogLevel)
    {
        _output = output;
        _minLogLevel = minLogLevel;
    }

    public void AddProvider(ILoggerProvider provider) => throw new NotSupportedException();
    public ILogger CreateLogger(string categoryName) => new XunitLogger(_output, _minLogLevel, categoryName);
    public void Dispose() { }
}

Create an extension method that will override the ILoggerFactory with the one for testing.

static class LoggingExtensions
{
   internal static IWebHostBuilder UseXunitLogging(this IWebHostBuilder builder, ITestOutputHelper output, LogLevel minLevel = LogLevel.Error) => builder.ConfigureTestServices(services =>
   {
       services.RemoveAll(typeof(ILoggerFactory));
       services.AddSingleton<ILoggerFactory>(new XUnitLoggingFactory(output, minLevel));
   });
}

Then the integration test will look like this:

public class ApplicationntegrationTests
{
    private readonly WebJobApplicationFactory _factory;

    public ApplicationntegrationTests(ITestOutputHelper output)
    {
        _factory = new WebJobApplicationFactory()
           .WithWebHostBuilder(builder => builder
                .UseXunitLogging(output));
    }

In the test runner, this is how will look the output:

One thing to notice is that the output will be displayed only once when the test fully finishes.

Conclusions

This approach replaces the standard application logging with xUnit-specific logging. If you prefer not to override but just want to capture logs in xUnit, consider using ILoggerProvider instead. This streamlined method ensures efficient and clear logging in your integration or component tests using xUnit.