user1574598
user1574598

Reputation: 3881

Testing the Output of a Logger - xUnit .NET Core 3.1

I'm trying to work out how to test that a message and log level are correct in my Serilog logging library.

I am using the Serilog.Sinks.TextWriter which gives me the output from Serilog as a string and when I print to the console I get these lines:

[16:44:03 INF] hello // actual log
2020-09-08 16:44:03.117 +01:00 [Information] hello // from StringWriter

I have this incomplete method:

[Fact]
public void LogInfo_InfoOutputCorrect()
{
    var logger = new Logger();

    const string message = "Test Info Log Output";
    const string level = "[Information]";

    logger.LogInfo(message); // log info method
    string returnMessage = logger.LogMessages(); // string from StringWriter
}

The message from the second line above I was thinking I could use the Contains string method to see if the message and log level match.

returnMessage.Contains(level) && returnMessage.Contains(message)

But not sure how to do this.

Upvotes: 2

Views: 3919

Answers (1)

Khior
Khior

Reputation: 1254

Here's a fairly quick and dirty solution for checking Serilog output. Of course you can develop this solution far beyond what I've presented here, but this should get you up and going.

    // The system under test
    public class MyImportantBehaviour
    {
        public void Run(Serilog.ILogger logger)
        {
            logger.Information("My important log message");
        }
    }

    // The test
    public class MyImportantBehaviourTests
    {
        [Fact]
        public void ExampleTest()
        {
            Queue<LogEvent> logEvents = new Queue<LogEvent>();

            Serilog.ILogger logger = new LoggerConfiguration()
                .MinimumLevel.Verbose()
                .WriteTo.InMemorySink(logEvents)
                .CreateLogger();

            new MyImportantBehaviour().Run(logger);

            logEvents.First().Level.Should().Be(LogEventLevel.Information);
            logEvents.First().MessageTemplate.Should().Be("My important log message");
        }
    }

    public sealed class InMemorySink : ILogEventSink
    {
        public Queue<LogEvent> LogEvents { get; }

        public InMemorySink(Queue<LogEvent> logEvents)
        {
            LogEvents = logEvents;
        }

        public void Emit(LogEvent logEvent)
        {
            LogEvents.Enqueue(logEvent);
        }
    }


    public static class InMemorySinkExtensions
    {
        public static LoggerConfiguration InMemorySink(this LoggerSinkConfiguration loggerConfiguration, Queue<LogEvent> logEvents)
        {
            return loggerConfiguration.Sink(new InMemorySink(logEvents));
        }
    }

If you're using the static logger (i.e. Log.Logger) then it becomes a bit more tricky. You can accomplish something similar by using the following, but you will likely run into issues if tests are run on multiple threads. Best to use dependency injection for your logger where you need this kind of testing.

    Log.Logger = new LoggerConfiguration()
                .MinimumLevel.Verbose()
                .WriteTo.InMemorySink(logLines)
                .CreateLogger();

Upvotes: 1

Related Questions