Unit Testing Static Methods with ILogger in C# ILoggerFactory is Null

1.7k Views Asked by At

everyone,

I'm trying to do unit tests of my code, however I'm not able to get the ILoggerFactory to work

This is my unit test code (it may not be correct):

using NUnit.Framework;
using Microsoft.Extensions.Logging;
using System.Reflection;
using Moq;
using System;
using MyProgramVIP.Bots.Presenter;
using MyProgramVIP.Bots.Model;
using MyProgramVIP.Bots.Utils;

namespace MyProgramVIPTest
{
    public class TestsExample
    {
        [SetUp]
        public void Setup() {
            
        }

        [Test]
        public void TestExample1()
        {
            //Mocks
            var mockLogger = new Mock<ILogger<TicketPresenter>>();
            mockLogger.Setup(
                m => m.Log(
                    LogLevel.Information,
                    It.IsAny<EventId>(),
                    It.IsAny<object>(),
                    It.IsAny<Exception>(),
                    It.IsAny<Func<object, Exception, string>>()));

            var mockLoggerFactory = new Mock<ILoggerFactory>();
            mockLoggerFactory.Setup(x => x.CreateLogger(It.IsAny<string>())).Returns(() => mockLogger.Object);

            //Construción del modelo necesario para la prueba
            ConversationData conversationData = new ConversationData();
            conversationData.ticket = new Ticket();
            conversationData.response = new Response();

            //Invocación del método a probar
            TicketPresenter.getPutTicketMessage(conversationData);

            //Comprobación del funcionamineto
            Assert.AreEqual("ticketType", conversationData.response.cardIdResponse);
        }
    }
}

This is the code of the class I want to test (I've only left the lines of code that fail)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
using MyProgramVIP.Bots.Interactor.Services;
using MyProgramVIP.Bots.Model;
using MyProgramVIP.Bots.Utils;

namespace MyProgramVIP.Bots.Presenter
{
    public class TicketPresenter
    {
        private static ILogger _logger = UtilsVIP.ApplicationLogging.CreateLogger(MethodBase.GetCurrentMethod().DeclaringType.Name);

        /// <summary>
        /// Función getPutTicketMessage: encargada de comenzar con el flujo de poner un ticket.
        /// </summary>
        /// <param name="conversationData"></param>
        public static void getPutTicketMessage(ConversationData conversationData)
        {
            try
            {
                //Here it crash.
                _logger.LogInformation("INIT getPutTicketMessage");

                //Code..........

                //Never gets here.
                _logger.LogInformation("ENDED getPutTicketMessage");
            }
            catch (Exception ex)
            {
                //Here it crash too.
                _logger.LogError("EXCEPTION getPutTicketMessage" + ex.ToString());
            }
        }
}

This is the code for the help class that's right where the error is:

using System;
using System.Collections.Generic;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
using MyProgramVIP.Bots.Model;
using MyProgramVIP.Bots.Model.Elements;

namespace MyProgramVIP.Bots.Utils
{
    public class UtilsVIP
    {
        private static ILogger _logger = ApplicationLogging.CreateLogger("ILogger");

        //Other funtions...

        public static class ApplicationLogging
        {
            public static ILoggerFactory LoggerFactory { get; set; }
            public static ILogger CreateLogger<T>() => LoggerFactory.CreateLogger<T>();
            //Here LoggerFactory is null.
            public static ILogger CreateLogger(string categoryName) => LoggerFactory.CreateLogger("VIPLog: " + categoryName);

        }

        //Other funtions...
    }
}

Right on the line where it runs:

LoggerFactory.CreateLogger("VIPLog: " + categoryName);

LoggerFactory is null.

I've done a lot of research on the internet, however it all leads to non-static class information.

In case it's important, it's a Botframework project.

Any help would be appreciated.

Thanks in advance.

2

There are 2 best solutions below

0
Stornu2 On BEST ANSWER

Thanks a lot to the comment off @ChetanRanpariya the answer to my question was really simple:

using NUnit.Framework;
using Microsoft.Extensions.Logging;
using System.Reflection;
using Moq;
using System;
using MyProgramVIP.Bots.Presenter;
using MyProgramVIP.Bots.Model;
using MyProgramVIP.Bots.Utils;

namespace MyProgramVIPTest
{
    public class TestsExample
    {
        [SetUp]
        public void Setup() {
            
        }

        [Test]
        public void TestExample1()
        {
            //Mocks
            var mockLogger = new Mock<ILogger<TicketPresenter>>();
            mockLogger.Setup(
                m => m.Log(
                    LogLevel.Information,
                    It.IsAny<EventId>(),
                    It.IsAny<object>(),
                    It.IsAny<Exception>(),
                    It.IsAny<Func<object, Exception, string>>()));

            var mockLoggerFactory = new Mock<ILoggerFactory>();
            mockLoggerFactory.Setup(x => x.CreateLogger(It.IsAny<string>())).Returns(() => mockLogger.Object);

            //Just add this, I guess is replacing the objet with the mock.
            UtilsVIP.ApplicationLogging.LoggerFactory = mockLoggerFactory.Object;
            
            //Construción del modelo necesario para la prueba
            ConversationData conversationData = new ConversationData();
            conversationData.ticket = new Ticket();
            conversationData.response = new Response();

            //Invocación del método a probar
            TicketPresenter.getPutTicketMessage(conversationData);

            //Comprobación del funcionamineto
            Assert.AreEqual("ticketType", conversationData.response.cardIdResponse);
        }
    }
}

Just add this:

UtilsVIP.ApplicationLogging.LoggerFactory = mockLoggerFactory.Object;

Thanks for the help.

0
Christian Findlay On

Here in example on how to verify that a log was called on ILogger with Moq.

 _loggerMock.Verify
        (
            l => l.Log
            (
                //Check the severity level
                LogLevel.Error,
                //This may or may not be relevant to your scenario
                It.IsAny<EventId>(),
                //This is the magical Moq code that exposes internal log processing from the extension methods
                It.Is<It.IsAnyType>((state, t) =>
                    //This confirms that the correct log message was sent to the logger. {OriginalFormat} should match the value passed to the logger
                    //Note: messages should be retrieved from a service that will probably store the strings in a resource file
                    CheckValue(state, LogTest.ErrorMessage, "{OriginalFormat}") &&
                    //This confirms that an argument with a key of "recordId" was sent with the correct value
                    //In Application Insights, this will turn up in Custom Dimensions
                    CheckValue(state, recordId, nameof(recordId))
            ),
            //Confirm the exception type
            It.IsAny<NotImplementedException>(),
            //Accept any valid Func here. The Func is specified by the extension methods
            (Func<It.IsAnyType, Exception, string>)It.IsAny<object>()),
            //Make sure the message was logged the correct number of times
            Times.Exactly(1)
        );

    private static bool CheckValue(object state, object expectedValue, string key)
    {
        var keyValuePairList = (IReadOnlyList<KeyValuePair<string, object>>)state;

        var actualValue = keyValuePairList.First(kvp => string.Compare(kvp.Key, key, StringComparison.Ordinal) == 0).Value;

        return expectedValue.Equals(actualValue);
    }

There is more context in this article.