Output logs to Xunit using Serilog Static Logger
Asked Answered
J

2

21

I use serilog in my projects with the static logger approach - it's nice and easy to be able to call Log.X on my class libraries rather than injecting a logger class everywhere.

However when it comes to unit/integration testing, if a test has failed it would be hugely beneficial to see the error logs from the class libraries (moreso for integration tests).

Because I am not injecting an ILogger into my classes (due to use of static logger), I can't create a mock test logger that writes output to the test logs.

Has anyone managed to output messages to XUnit using the Serilog global (static) logger?

Jasik answered 18/11, 2019 at 11:38 Comment(1)
put github.com/jet/equinox/blob/… into Log.Logger (Including a WriteTo.Seq() can be super useful too)Fog
P
35

The Serilog.Sinks.XUnit nuget package makes it easy to accomplish this. Reference the nuget in your project. Then you can use the static logger in the test:

using Serilog;
using Xunit;
using Xunit.Abstractions;

namespace XunitToSerilog
{
    public class SampleTest
    {
        public SampleTest(ITestOutputHelper output)
        {
            Log.Logger = new LoggerConfiguration()
            // add the xunit test output sink to the serilog logger
            // https://github.com/trbenning/serilog-sinks-xunit#serilog-sinks-xunit
            .WriteTo.TestOutput(output)
            .CreateLogger();
        }

        [Fact]
        public void Test1()
        {
            Log.Information("goes to test output");
        }
    }
}
Petrinapetrine answered 2/1, 2020 at 16:49 Comment(3)
Here initialization of Serilog+xUnit happens in a test class constructor. So you have to write the same initialization logic in each test class file? Is it possible to do it at program entry point globally?Elenore
@Elenore The key point here ist the connection of the ITestOutputHelper output to the logger. ITestOutputHelper is injected by XUnit into the class constructor. This design decision makes sense so that the test runner can assign the output to a specific Test. You could derive all your test classes from a base class which is doing the initialization so that you write less code. Or maybe you find another way using Shared Context between Tests see CollectionFixture.Petrinapetrine
I think what would actually be nice is connecting the output during dependency injection phase, like in the startup ConfigureServices method. You can do something like loggerFactory.AddProvider and specify the XunitTestOutputLoggerProvider, but I want to actually specify a serilog provider. no idea how to do this.Skutchan
S
0

If, on the other hand, you want to test the output of the logs, a quick and dirty way might be:

using FluentAssertions;
using Serilog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Xunit;
using Xunit.Abstractions;
using Xunit.Sdk;

namespace TestNamespace;

public class TestClass
{
    private readonly TestOutputHelper _logOutput;

    public TestClass(ITestOutputHelper logOutput)
    {
        _logOutput = (TestOutputHelper)logOutput;

        Log.Logger = new LoggerConfiguration()
            .WriteTo.TestOutput(_logOutput)
            .CreateLogger();
    }

    [Fact]
    public async Task TestMethodShould()
    {
        var foo = "bar";
    
        // method under test should use serilog to Log.Warn(...);
        var result = await classUnderTest.MethodUnderTest(foo);
        
        _logOutput.Output.Should().NotBeNullOrEmpty();
        _logOutput.Output.Trim().Split("\n").Should().HaveCount(1);
        _logOutput.Output.Should().Contain("WRN] log message that should return");
    }
}
Salmanazar answered 19/1, 2023 at 13:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.