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.

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

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();


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x