0

Net core 和 NUnit 测试用例。我的控制器如下。

public class MyController : ControllerBase
    {
      public MyController(ILogger<MyController > logger)
        {
            this.Logger = logger ?? throw new ArgumentNullException(nameof(logger));
        }
    private readonly ILogger<MyController > Logger;

    public async Task<ActionResult> GetList()
    {
      this.Logger.LogInformation($"MyList: Some results came");
    }
 }

然后在我的单元测试用例下方。

 public class MyControllerTests
 {
   private Mock<ILogger<MyController>> Logger = new Mock<ILogger<MyController>>();
   internal MyController myController;
   public MyControllerTests()
        {
            this.myController= new MyController(this.Logger.Object);
        }
    [Test]
        public async Task ListTest()
        {
           Logger.Verify(
               x => x.Log(
                   LogLevel.Information,
                   It.IsAny<EventId>(),
                   It.Is<It.IsAnyType>((o, t) => string.Equals("MyList: Some results came", o.ToString(), StringComparison.InvariantCultureIgnoreCase)),
                   It.IsAny<Exception>(),
                   (Func<It.IsAnyType, Exception, string>)It.IsAny<object>()),
         //rest of the code but i am getting error in above line
        }
 }

上面的代码给出了错误

Moq.MockException : ILogger.Log(LogLevel.Information, 0, ListTest : 成功获取结果, null, Func<FormattedLogValues, Exception, string>) 调用失败,模拟行为严格。模拟上的所有调用都必须具有相应的设置。

有人可以帮我解决这个问题。任何帮助将不胜感激。谢谢

4

2 回答 2

1

你很亲密。这是一篇关于此的文章。这是我提出的两个工作验证:

        _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, "MyList: Some results came", "{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)
        );
于 2020-07-08T23:38:54.273 回答
0

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

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

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

基本上你会按如下方式进行:

默认使用 NullLogger:

public class MyController : ControllerBase
{        
    private ILogger _logger = NullLogger.Instance;
    
    protected MyController(ILogger<MyController> 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(MyController));

            // 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<MyController>();
            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}" : "")}";
        }
    }
}
于 2020-07-13T18:42:06.557 回答