【问题标题】:ASP.NET Unit Testing - Mocking ILogger in unit testsASP.NET 单元测试 - 在单元测试中模拟 ILogger
【发布时间】:2019-12-13 08:55:56
【问题描述】:

我目前正在使用以下代码在我的单元测试控制器的构造器中模拟 ILogger:

private readonly Mock<ILogger> _logger = new Mock<ILogger>();

我需要使用这个 Mock ILogger 来记录在各种单元测试中被断言的抛出异常。

例如:

    [Test]
    public void Arguments_CallBoardStatsRepo_Null()
    {
        Assert.Throws<NullReferenceException>(() => new AgentsController(null, _clientCallsRepoMock.Object, _agentStatusRepoMock.Object, _logger.Object));
       _logger.Verify(m => m.Error("Error", It.IsAny<NullReferenceException>()), Times.Once);

    }

我需要为模拟的记录器(_logger)添加 ArgumentNullException 检查。

实现这一目标的最佳方法是什么?

编辑:正在测试的控制器

public class AgentsController : ApiController
{
    readonly IAgentStatusRepo _agentStatusRepo;
    readonly ICallBoardStatsRepo _callBoardRepo;
    readonly IClientCallsRepo _clientCallRepo;
    readonly ILogger _logger;

    public AgentsController(ICallBoardStatsRepo callBoardRepo,
        IClientCallsRepo clientCallRepo,
        IAgentStatusRepo agentStatusRepo,
        ILogger logger)
    {
        Util.Guard.ArgumentsAreNotNull(callBoardRepo, clientCallRepo, agentStatusRepo);

        _callBoardRepo = callBoardRepo;
        _clientCallRepo = clientCallRepo;
        _agentStatusRepo = agentStatusRepo;
        _logger = logger;
    }

    [HttpGet]
    [Route("api/agents")]
    public IHttpActionResult FindAllAgentsByClientGroup(string group)
    {
        IEnumerable<AgentStatus> agentCallStats = _agentStatusRepo.ByGroupKey(group).ToList();
        return Ok(agentCallStats);
    }
}

【问题讨论】:

    标签: asp.net unit-testing logging ilogger


    【解决方案1】:

    这是关于该主题的article。它讨论了如何使用 Moq 模拟和验证 ILogger 调用。这是一个简单的例子:

            _logTest.Process();
            _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));
    

    这是一个更复杂的示例,用于验证有关通话的具体细节。

            _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<ArgumentNullException>(),
                //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)
            );
    

    注意可以查看上面的异常类型

    【讨论】:

      【解决方案2】:

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

      使用示例可在此处获得:https://github.com/akovac35/Logging.Samples

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

      测试记录器将日志条目发送到您可以查询特定消息的测试接收器,类似于以下代码 sn-p:

      [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<AgentsController>();
          controller.Invoke();
      
          var testSink = serviceProvider.GetRequiredService<ITestSink>();
      
          Assert.IsTrue(testSink.Writes.Count > 0);
          Assert.IsTrue(testSink.Scopes.Count > 0);
      }
      

      设置示例:

      默认使用 NullLogger:

      public class AgentsController: ControllerBase
      {        
          private ILogger _logger = NullLogger.Instance;
          
          protected AgentsController(ILogger<AgentsController> logger = null)
          {
              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(AgentsController));
                  // Register mocks as you would any other service ...
      
                  // 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<AgentsController>();
                  controller.Invoke();
      
                  var testSink = serviceProvider.GetRequiredService<ITestSink>();
      
                  Assert.IsTrue(testSink.Writes.Count > 0);
                  Assert.IsTrue(testSink.Scopes.Count > 0);
              }
          }
      }
      

      testSink.Writes 包含可以断言特定日志消息的 WriteContext 对象:

      using Microsoft.Extensions.Logging;
      using System;
      
      namespace com.github.akovac35.Logging.Testing
      {
          [System.Diagnostics.DebuggerDisplay("{ToString()}")]
          public class WriteContext
          {
              public LogLevel LogLevel { get; set; }
      
              public EventId EventId { get; set; }
      
              public object State { get; set; }
      
              public Exception Exception { get; set; }
      
              public Func<object, Exception, string> Formatter { get; set; }
      
              public ScopeContext Scope { get; set; }
      
              public ILogger Logger { get; set; }
      
              public DateTime Timestamp { get; set; }
      
              public int ThreadId { get; set; }
      
              public virtual string Message
              {
                  get
                  {
                      return Formatter(State, Exception);
                  }
              }
      
              public override string ToString()
              {
                  return $"[{Timestamp:yyyy-MM-dd HH:mm:ss.fff}] {ThreadId} {LogLevel} EventId: {EventId}{(Scope != null ? $" Scope: <{Scope}>" : "")} Message: {Message}{(Exception != null ? $"{Environment.NewLine}{Exception}" : "")}";
              }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2015-05-10
        • 1970-01-01
        • 2023-03-10
        • 1970-01-01
        • 2013-02-22
        • 2011-04-11
        • 2020-05-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多