Unit Test - How to test a `void` method that just inserts a log message (Serilog)

Viewed 4102

I need to test a method of type void, it just inserts a message on my LOG variable using a LOG framework (Serilog).

See the implementation example:

public class MyClass
{
    public MyClass(ILogger<IProcess> logger)
    {
        this.logger = logger;
    }

    private readonly ILogger logger;

    //...Anothers methods...

    public void LogInit(Guid processId, string folder, string[] args)
    {
        var myObject = new
        {
            ProcessId = processId,
            Folder = folder,
            Arguments = args
        };

        this.logger.LogWarning("{@myObject}", myObject);
    }
}

In this scenario, we would need to create a test for the LogInit (...)

Does this method really need to be tested? Or rather, does it make sense for it to exist?

I was reading something about:

"If your method has no side effects, and doesn’t return anything, then it’s not doing anything."

In this case, the method was only created "separately" to maintain the organization of the code and separate the responsibilities of the class a bit more.

That makes sense?

EDIT: @PeterBons

We do not have access to the messages registered in the Interface (logger), like: this.logger.GiveMeWarningLogs()...

3 Answers

If you're using Serilog, you can use the Test Correlator Sink to see what log events a method call produced.

[TestMethod]
public void A_test()
{
    var myClass = new MyClass(new LoggerConfiguration().WriteTo.TestCorrelator().CreateLogger());

    using (TestCorrelator.CreateContext())
    {
        myClass.LogInit();

        TestCorrelator.GetLogEventsFromCurrentContext()
            .Should().ContainSingle()
            .Which.MessageTemplate.Text
            .Should().Be("{@myObject}");
    }
}

You have to mock your logger and check whether LogWarning method was called. You can use Moq for this. Also if you want to test LogInit you need to make this public or internal with defining [InternalVisibleTo('someTestProjName')]

Test method will looks like this (xUnit):

public void ShouldCallLogWarning()
{
    var loggerMock = new Mock<ILogger>();
    loggerMock.Setup(_ => _.LogWarning(It.IsAny<string>(), It.IsAny<object>(), null);        

    var myClass = new MyClass(loggerMock.Object);

    //
    myClass.LogInit(Guid.NewGuid(), "folderPath", null)

    //
    _loggerMock.Verify(_ => _.LogWarning(It.IsAny<string>(), It.IsAny<string>(), null), Times.Once());
}

Your method is not returning anything so it would be harder to test if for sure. Right now the only thing your method does is prepare the logged object for the Serilog library. If you had more complicated logic for the creation of that object you could extract it into it's own method and have it return that log object then it would be easy to test that instead. The benefit of the method you have created is that it creates an extra layer so that if you decide to change the logging library you would only do it in one place.

Related