Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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

like image 239
Igor Avatar asked Oct 20 '18 17:10

Igor


People also ask

How do you mock method which returns void in C#?

Mockito provides following methods that can be used to mock void methods. doAnswer() : We can use this to perform some operations when a mocked object method is called that is returning void. doThrow() : We can use doThrow() when we want to stub a void method that throws exception.

How do you mock an ILogger?

To mock an ILogger<T> object, we can use Moq library to instantiate a new Mock<ILogger<T>>() object. Make sure you have installed the latest Moq package (v4. 15.1 as of Nov. 16, 2020, which contains an update to support “nested” type matchers).


2 Answers

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());
}
like image 29
svoychik Avatar answered Sep 23 '22 08:09

svoychik


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}");
    }
}
like image 172
gitbox Avatar answered Sep 21 '22 08:09

gitbox