带ILogger的单元测试基础控制器

时间:2019-02-18 12:59:56

标签: c# unit-testing asp.net-core

我的网络核心api中有一个基本控制器(我并没有创建它),基本上从以下内容开始:

public abstract class MyBaseController<T> : ControllerBase where T : MyBaseController<T>
{        
    private ILogger<T> _logger;
    protected ILogger<T> Logger => _logger ?? (_logger = HttpContext?.RequestServices.GetService<ILogger<T>>());
}

当我对继承基本控制器的其他控制器进行单元测试时,如何处理此记录器?

目前,我的单元测试班有一个类似的构造器

_controller = new cartController(_cartService);

但是我被困住了。

我将在测试项目中使用xUnit和Moq。

感谢您的帮助。谢谢

2 个答案:

答案 0 :(得分:1)

这是this article中的一个最小示例,说明如何注入ILogger依赖项,然后使用Moq验证调用。

public class LogTest
{
    private readonly ILogger _logger;
    public const string InformationMessage = "Test message";
    public const string ErrorMessage = "Not implemented {recordId}";

    public LogTest(ILogger<LogTest> logger)
    {
        _logger = logger;
    }

    public void Process()
    {
        _logger.LogInformation(InformationMessage);
    }
}

_loggerMock.Verify(l => l.Log(
    LogLevel.Information,
    It.IsAny<EventId>(),
    It.IsAny<It.IsAnyType>(),
    It.IsAny<Exception>(),
    (Func<It.IsAnyType, Exception, string>)It.IsAny<object>()), Times.Exactly(1));

答案 1 :(得分:0)

通常,在测试以及运行时中都应依赖DI。以下库包含您可以在测试中使用的测试记录器:https://www.nuget.org/packages/com.github.akovac35.Logging.Testing/

此处提供了使用示例:https://github.com/akovac35/Logging.Samples

免责声明:我是以上内容的作者。

基本上,您将按照以下步骤操作:

默认使用NullLogger:

public abstract class MyBaseController<T> : ControllerBase
{        
    private ILogger _logger = NullLogger.Instance;
    
    protected MyBaseController(ILogger<MyBaseController<T>> logger = null)
    {
        if (logger != null) _logger = logger;
    }
}

派生类应注入记录器:

public class MyBaseControllerVariant<T> : MyBaseController<T>
{        
    private ILogger _logger = NullLogger.Instance;    

    public MyBaseControllerVariant(ILogger<MyBaseControllerVariant<T>> logger = null, ILogger<MyBaseController<T>> baseLogger = null): base(baseLogger)
    {
        if (logger != null) _logger = logger;
    }

}

现在将所有内容连接起来

using com.github.akovac35.Logging.Testing;
using Microsoft.Extensions.DependencyInjection;
using NUnit.Framework;
using Shared.Mocks;
using System;

namespace TestApp
{
    [TestFixture]
    public class TestLoggingExamples
    {
        [OneTimeSetUp]
        public void OneTimeSetUp()
        {
            customOnWrite = writeContext => {
                Console.WriteLine(writeContext);
            };

            customOnBeginScope = scopeContext => {
                Console.WriteLine(scopeContext);
            };

            serviceCollection = new ServiceCollection();
            serviceCollection.AddTransient(typeof(MyBaseControllerVariant<>));

            // Register TestLogger using extension method
            serviceCollection.AddTestLogger(onWrite: customOnWrite, onBeginScope: customOnBeginScope);
        }

        private IServiceCollection serviceCollection;

        private Action<WriteContext> customOnWrite;
        private Action<ScopeContext> customOnBeginScope;

        [Test]
        public void Test_WithLoggingToTestConsole_Works()
        {
            // The service provider should be defined on per-test level or logger writes will accumulate and may result in OOM - clean them with testSink.Clear()
            var serviceProvider = serviceCollection.BuildServiceProvider();
            var controller = serviceProvider.GetRequiredService<MyBaseControllerVariant<object>>();
            controller.Invoke();

            var testSink = serviceProvider.GetRequiredService<ITestSink>();

            Assert.IsTrue(testSink.Writes.Count > 0);
            Assert.IsTrue(testSink.Scopes.Count > 0);
        }
    }
}