System.NotSupportedException: Unsupported expression: m => m.LogDebug(It.IsAny<string>(), new[] { })
System.NotSupportedException Unsupported expression: m => m.LogDebug(It.IsAny<string>(), new[] { }) Extension methods (here: LoggerExtensions.LogDebug) may not be used in setup / verification expressions. at Moq.Guard.IsOverridable(MethodInfo method, Expression expression) in C:\projects\moq4\src\Moq\Guard.cs:line 87 at Moq.MethodExpectation..ctor(LambdaExpression expression, MethodInfo method, IReadOnlyList`1 arguments, Boolean exactGenericTypeArguments, Boolean skipMatcherInitialization, Boolean allowNonOverridable) in C:\projects\moq4\src\Moq\MethodExpectation.cs:line 86 at Moq.ExpressionExtensions.<Split>g__Split|5_0(Expression e, Expression& r, MethodExpectation& p, Boolean assignment, Boolean allowNonOverridableLastProperty) in C:\projects\moq4\src\Moq\ExpressionExtensions.cs:line 235 at Moq.ExpressionExtensions.Split(LambdaExpression expression, Boolean allowNonOverridableLastProperty) in C:\projects\moq4\src\Moq\ExpressionExtensions.cs:line 149 at Moq.Mock.GetMatchingInvocationCount(Mock mock, LambdaExpression expression, List`1& invocationsToBeMarkedAsVerified) in C:\projects\moq4\src\Moq\Mock.cs:line 432 at Moq.Mock.Verify(Mock mock, LambdaExpression expression, Times times, String failMessage) in C:\projects\moq4\src\Moq\Mock.cs:line 318 at Moq.Mock`1.Verify(Expression`1 expression) in C:\projects\moq4\src\Moq\Mock`1.cs:line 713 at aspnetcore_logger_ut_moq.DemoTests.TestDoSomething() in D:\CodeLab\aspnetcore-logger-ut-moq\aspnetcore-logger-ut-moq\aspnetcore-logger-ut-moq\DemoTests.cs:line 20 at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor) at System.Reflection.MethodInvoker.Invoke(Object obj, IntPtr* args, BindingFlags invokeAttr)
看下logger.LogDebug()的源码,的确是一个扩展方法,
1 2 3 4 5 6 7 8 9 10 11 12
namespaceMicrosoft.Extensions.Logging { ///<summary>ILogger extension methods for common scenarios.</summary> publicstaticclassLoggerExtensions { ///<summary>Formats and writes a debug log message.</summary> ///<param name="logger">The <see cref="T:Microsoft.Extensions.Logging.ILogger" /> to write to.</param> ///<param name="message">Format string of the log message in message template format. Example: <c>"User {User} logged in from {Address}"</c></param> ///<param name="args">An object array that contains zero or more objects to format.</param> publicstaticvoidLogDebug(this ILogger logger, string? message, paramsobject?[] args) => logger.Log(LogLevel.Debug, message, args); } }
看下来最终是调用了ILogger.Log方法logger.Log<FormattedLogValues>(logLevel, eventId, new FormattedLogValues(message, args), exception, LoggerExtensions._messageFormatter);,那我们就得对这个方法进行Verify。
namespaceMicrosoft.Extensions.Logging { ///<summary>Represents a type used to perform logging.</summary> publicinterfaceILogger { ///<summary>Writes a log entry.</summary> ///<param name="logLevel">Entry will be written on this level.</param> ///<param name="eventId">Id of the event.</param> ///<param name="state">The entry to be written. Can be also an object.</param> ///<param name="exception">The exception related to this entry.</param> ///<param name="formatter">Function to create a <see cref="T:System.String" /> message of the <paramref name="state" /> and <paramref name="exception" />.</param> ///<typeparam name="TState">The type of the object to be written.</typeparam> void Log<TState>( LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter); } }
于是将单元测试来改造如下后,发现FormattedLogValues报错:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
[Fact] publicvoidTestDoSomething() { // Given Mock<ILogger<Demo>> loggerMock = new(); var sut = new Demo(loggerMock.Object);
publicvoidDoSomething() { _logger.LogDebug("This is a debug log");
_logger.LogInformation("this is a info log"); _logger.LogWarning("this is a warning log"); }
测试的断言写起来也很简洁:
1 2 3
loggerMock.VerifyLogging("This is a debug log") .VerifyLogging("this is a info log", LogLevel.Information) .VerifyLogging("this is a warning log", LogLevel.Warning, Times.Once());