【发布时间】:2020-06-26 07:15:56
【问题描述】:
我有一个 .Net Core 3 应用程序,正在尝试在我的方法中测试对 ILogger 的调用:
public class MyClass
{
private readonly ILogger<MyClass> _logger;
public MyClass(ILogger<MyClass> logger)
{
_logger = logger;
}
public void MyMethod(string message)
{
_logger.LogError(message);
}
}
在 SO 和博客上找到答案后,我知道我必须针对接口方法进行测试,而不是扩展方法,所以我有这个测试:
[TestMethod]
public void MyMethodTest()
{
// Arrange
var logger = Substitute.For<ILogger<MyClass>>();
var myClass = new MyClass(logger);
var message = "a message";
// Act
myClass.MyMethod(message);
// Assert
logger.Received(1).Log(
LogLevel.Error,
Arg.Any<EventId>(),
Arg.Is<object>(o => o.ToString() == message),
null,
Arg.Any<Func<object, Exception, string>>());
}
但是,这不起作用,我收到此错误:
Test method MyLibrary.Tests.MyClassTests.MyMethodTest threw exception:
NSubstitute.Exceptions.ReceivedCallsException: Expected to receive exactly 1 call matching:
Log<Object>(Error, any EventId, o => (o.ToString() == value(MyLibrary.Tests.MyClassTests+<>c__DisplayClass0_0).message), <null>, any Func<Object, Exception, String>)
Actually received no matching calls.
at NSubstitute.Core.ReceivedCallsExceptionThrower.Throw(ICallSpecification callSpecification, IEnumerable`1 matchingCalls, IEnumerable`1 nonMatchingCalls, Quantity requiredQuantity)
at NSubstitute.Routing.Handlers.CheckReceivedCallsHandler.Handle(ICall call)
at NSubstitute.Routing.Route.Handle(ICall call)
at NSubstitute.Core.CallRouter.Route(ICall call)
at NSubstitute.Proxies.CastleDynamicProxy.CastleForwardingInterceptor.Intercept(IInvocation invocation)
at Castle.DynamicProxy.AbstractInvocation.Proceed()
at NSubstitute.Proxies.CastleDynamicProxy.ProxyIdInterceptor.Intercept(IInvocation invocation)
at Castle.DynamicProxy.AbstractInvocation.Proceed()
at Castle.Proxies.ObjectProxy.Log[TState](LogLevel logLevel, EventId eventId, TState state, Exception exception, Func`3 formatter)
at MyLibrary.Tests.MyClassTests.MyMethodTest() in D:\Source\Scratch\MyLibrary\MyLibrary.Tests\MyClassTests.cs:line 25
我做错了什么?
netcoreapp3.0 / Microsoft.Extensions.Logging 3.1.2 / NSubstitute 4.2.1
更新:我已经尝试与Arg.Any<>() 匹配并得到相同的结果:
logger.Received(1).Log(
Arg.Any<LogLevel>(),
Arg.Any<EventId>(),
Arg.Any<object>(),
Arg.Any<Exception>(),
Arg.Any<Func<object, Exception, string>>());
更新 2:我使用 Moq 尝试了相同的测试并得到相同的结果:
logger.Verify(l => l.Log(
LogLevel.Error,
It.IsAny<EventId>(),
It.Is<object>(o => o.ToString() == message),
null,
It.IsAny<Func<object, Exception, string>>()),
Times.Once);
结果:
Test method MyLibrary.Tests.Moq.MyClassTests.MyMethodTest threw exception:
Moq.MockException:
Expected invocation on the mock once, but was 0 times: l => l.Log<object>(LogLevel.Error, It.IsAny<EventId>(), It.Is<object>(o => o.ToString() == "a message"), null, It.IsAny<Func<object, Exception, string>>())
Performed invocations:
Mock<ILogger<MyClass>:1> (l):
ILogger.Log<FormattedLogValues>(LogLevel.Error, 0, a message, null, Func<FormattedLogValues, Exception, string>)
at Moq.Mock.Verify(Mock mock, LambdaExpression expression, Times times, String failMessage)
at Moq.Mock`1.Verify(Expression`1 expression, Times times)
at Moq.Mock`1.Verify(Expression`1 expression, Func`1 times)
at MyLibrary.Tests.Moq.MyClassTests.MyMethodTest() in D:\Source\Scratch\MyLibrary\MyLibrary.Tests.Moq\MyClassTests.cs:line 25
【问题讨论】:
-
您的代码调用
LogError(),而您正在验证对Log()的调用... -
LogError是一种扩展方法,最终链接到Log- 正如我所说,这是许多博客文章和其他 SO 问题中给出的示例代码 -
@haim770 对此表示感谢...它确实可以说明问题,但我不确定如何继续...在静态扩展上使用某种有效的验证,或将 ILogger 包装在我自己的界面中。 ..两者都不是好的选择。 MELT 解决方案看起来很有趣,但在我的完整实现中,我实际上使用的是 AutoFixture.Freeze,所以不确定它是否合适
-
我已经选择了“头在沙子里”选项并针对扩展进行了测试。当/如果将来测试中断时,我会担心它
标签: unit-testing .net-core mstest nsubstitute ilogger