How to log TestServer output to the test console

Viewed 1796

I'm currently writing an integration test (https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-5.0) for my ASP .Net Core 5 REST API. The API is using Serilog for logging (with the static Serilog Logger). I am running tests with NUnit, Visual Studio 2019, Resharper.

I want all the messages, that are logged during the runtime of the API code, to be visible in the test console output.
For example, if this controller method is called:

using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Serilog;

namespace My.Crazy.Api.Controllers
{    
    public sealed class WheelsController : Controller
    {
        [HttpGet("getwheels")]        
        public async Task<IActionResult> Get()
        {            
            Log.Error("An extremely urgent error");         
            return Ok();
        }
    }
}

I expect the "An extremely urgent error" message to be shown in the test console.
However, this is not happening.

Here is my TestServer setup:

[OneTimeSetUp]
public async Task Setup()
{            
    var hostBuilder = new HostBuilder()
        .ConfigureWebHost(webHost =>
        {
            webHost.UseTestServer();
            webHost.UseStartup<Startup>();  // Startup is the API project's Startup class
    
            Log.Logger = new LoggerConfiguration().WriteTo.Console().CreateLogger();
        });

    var host = await hostBuilder.StartAsync();
    
    _client = host.GetTestClient();
}  

[Test]
public async Task FirstTest() 
{
    var response = await _client.GetAsync("getwheels");
}

I have also tried logging with a custom Sink:

...
// in the test setup
Log.Logger = new LoggerConfiguration().WriteTo.Sink(new CustomSink()).CreateLogger();
...

public class CustomSink : ILogEventSink
{
    public void Emit(LogEvent logEvent)
    {
        var message = logEvent.RenderMessage();
        Console.WriteLine(message);
    }
}

This does not work as well. However, I have confirmed that the Emit method is being invoked when API code logs any message.

Finally, I have tried using a File output:

Log.Logger = new LoggerConfiguration().WriteTo.File("C:\\temp\\test_output.txt").CreateLogger();

which worked as expected. However, I still want to log in the console.

Is this possible?

Using anything else for Serilog or NUnit is unfortunately not an option.

1 Answers

So I would try with a custom logger provider with logger:

LoggerProvider:

public class NUnitLoggerProvider : ILoggerProvider
{
    public ILogger CreateLogger(string categoryName)
    {
        return new NUnitLogger();
    }

    public void Dispose()
    {
    }
}

Logger:

public class NUnitLogger : ILogger, IDisposable
{
    public void Dispose()
    {
    }

    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception,
        Func<TState, Exception, string> formatter) {
        var message = formatter(state, exception);
        Debug.WriteLine(message);
    }

    public bool IsEnabled(LogLevel logLevel) => true;

    public IDisposable BeginScope<TState>(TState state) => this;
}    

Then in the test file:

var hostBuilder = new HostBuilder()
            .ConfigureWebHost(webHost =>
            {
                webHost.UseTestServer()
                    .UseStartup<TestStartup>()
                    .ConfigureLogging((hostBuilderContext, logging) =>
                     {
                         logging.Services.AddSingleton<ILoggerProvider, NUnitLoggerProvider>();
                     });
            });            

And instead of Debug.WriteLine(message) you can use something else to log to.

Related