.net core 2.0 ConfigureLogging xunit test
Asked Answered
U

1

25

In my xUnit integration test in my .NET Core 2.0 project I cannot see log messages in the terminal that also prints the test results. When the code is run in WebHost environment, the logs are printed out to the console.

This is how I configure logging in the test's constructor:

var config = new ConfigurationBuilder()
    .AddJsonFile("appsettings.Tests.json")
    .Build();

var services = new ServiceCollection();

services.AddLogging(options => {
    options.AddConfiguration(config.GetSection("Logging"));
    options.AddConsole();
    options.AddDebug();
});

var serviceProvider = services.BuildServiceProvider();
var loggerFactory = serviceProvider.GetService<ILoggerFactory>();
var logger = loggerFactory.CreateLogger("Test");
logger.LogError("From ctor");

But I don't see any log message.

Unbelieving answered 12/9, 2017 at 6:15 Comment(0)
U
57

xUnit has changed in version 2 to no longer capture the standard output for tests:

If you used xUnit.net 1.x, you may have previously been writing output to Console, Debug, or Trace. When xUnit.net v2 shipped with parallelization turned on by default, this output capture mechanism was no longer appropriate; it is impossible to know which of the many tests that could be running in parallel were responsible for writing to those shared resources.

Instead, you are now supposed to use an explicit mechanism to write to the test output. Basically, instead of writing to the console, you are writing to a special ITestOutputHelper.

Of course, this output mechanism is not supported by default with ASP.NET Core logging. Fortunately, writing a logging provider for the test output isn’t too difficult. I’ve just implemented a quick provider and included it in my answer below. You can use it like this:

public class Example
{
    private readonly ILogger<Example> _logger;

    public Example(ITestOutputHelper testOutputHelper)
    {
        var loggerFactory = LoggerFactory.Create(l =>
        {
            l.AddProvider(new XunitLoggerProvider(testOutputHelper));
        });
        _logger = loggerFactory.CreateLogger<Example>();
    }

    [Fact]
    public void Test()
    {
        _logger.LogDebug("Foo bar baz");
    }
}

Note that you usually should avoid creating a complete dependency injection container in unit tests. Having DI in unit tests is usually a sign that your unit test is not a unit test but instead an integration test. In a unit test, you should only test one particular unit and pass all its dependencies explicitly—more than often just as a mock. As you can see in the example above, creating a logger is actually a very simple thing to do without DI.


As promised, this is the XunitLoggerProvider and the XunitLogger which you need to run the code shown above, and to integrate the Microsoft.Extensions.Logging framework with xUnit test output:

public class XunitLoggerProvider : ILoggerProvider
{
    private readonly ITestOutputHelper _testOutputHelper;

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

    public ILogger CreateLogger(string categoryName)
        => new XunitLogger(_testOutputHelper, categoryName);

    public void Dispose()
    { }
}

public class XunitLogger : ILogger
{
    private readonly ITestOutputHelper _testOutputHelper;
    private readonly string _categoryName;

    public XunitLogger(ITestOutputHelper testOutputHelper, string categoryName)
    {
        _testOutputHelper = testOutputHelper;
        _categoryName = categoryName;
    }

    public IDisposable BeginScope<TState>(TState state)
        => NoopDisposable.Instance;

    public bool IsEnabled(LogLevel logLevel)
        => 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 != null)
            _testOutputHelper.WriteLine(exception.ToString());
    }

    private class NoopDisposable : IDisposable
    {
        public static readonly NoopDisposable Instance = new NoopDisposable();
        public void Dispose()
        { }
    }
}
Unpractical answered 12/9, 2017 at 9:34 Comment(11)
Thank you. 1) I know that it is not a unit test. What I want to do in my integration test here is to see the warnings that Entity Framework logs when some queries are evaluated locally. I will update the question. 2) I hooked your classes up. The Log method of XunitLogger is invoked and I see badly formatted output in the OmniSharp Log but I don't see it neither in .NET Test Log nor in the terminal's output when I run dotnet test.Unbelieving
@Max To see the output from the console runner, you need to use the dotnet-xunit CLI tool (dotnet test is not powerful enough for that) and run ` dotnet xunit -diagnostics`. The output will automatically appear properly in Visual Studio though.Unpractical
So nothing like this is included still? (just asking since this is over a year old now)Panama
@Panama No, xUnit will still not catch console output (for the same reasons), and a built-in logger is not provided.Unpractical
how do I get this logger output to show up in visual studio 2017 [ / 2019 ] | output | tests pane when I run [ or debug ] a .net core xunit test?Liles
@Liles Check the linked xUnit docs, there’s a screenshot of how it looks. You will have to click on the “Output” link of the test result in the test explorer panel.Unpractical
@Unpractical thanks for response. Using the dependency injected ITestOutputHelper output.WriteLine(), and this derived ILogger log.LogInformation() approach, option does result in an "Output" link in the test result pane displayed in bottom left corner of test explorer. I'm using [Theory, Class/MemberData()] data driven tests so my preference is a solution that outputs each data set run to the Output pane's "Tests" view, displayed by default following automated xUnit test run and debug test execution cases, so I can see it without clicks for each data set. Is there no way to make that happen?Liles
@Liles No, not really. xUnit supports parallel execution and any output mechanism that writes to a single target wouldn’t work for that.Unpractical
Hi, I m using TestServerFixture, I would like to add Xunit like to suggest to my WebHostBuilder but I dont find a way to resolve ITestOutputHelper from TestServerFixture or WebHostBuilder.ConfigureLogging(). Do you have any idea how to do that?Jespersen
@CedricArnould no doubt you solved this by now :) but for anyone that finds this, I did what you are asking like so: ` webHostBuilder.ConfigureLogging(loggingBuilder => { loggingBuilder.AddProvider(new XUnitLoggerProvider(this.xUnitLogger)); loggingBuilder.SetMinimumLevel(LogLevel.Debug); }); `Davidadavidde
My logger is setup against my ServicesFixture, so instead of ITestOutputHelper it needs to be IMessageSink though.Bremer

© 2022 - 2024 — McMap. All rights reserved.