我目前正在使用以下代码在单元测试控制器的构造器中模拟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);
}
}
答案 0 :(得分:0)
根据我的观察,所显示的测试类中甚至没有使用 looger ,因此没有任何要验证的模拟。
请注意,Assert.Throws
返回抛出的异常,因此您应该能够做到
var exception =
[Test]
public void Arguments_CallBoardStatsRepo_Null() {
//Act
Action act = () => new AgentsController(null, _clientCallsRepoMock.Object, _agentStatusRepoMock.Object, _logger.Object);
//Assert
ArgumentNullException exception = Assert.Throws<ArgumentNullException>(act);
//...inspect exception as desired.
}
答案 1 :(得分:0)
这是关于该主题的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 :(得分:0)
通常,在测试以及运行时中都应依赖DI。以下库包含您可以在测试中使用的测试记录器:https://www.nuget.org/packages/com.github.akovac35.Logging.Testing/
此处提供了使用示例:https://github.com/akovac35/Logging.Samples
免责声明:我是以上内容的作者。
测试记录器将日志条目发送到测试接收器,您可以查询特定消息,类似于以下代码片段:
[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}" : "")}";
}
}
}