Mock ILogger及验证方法调用时遇到的异常问题
解决Moq验证ILogger.LogError时的NotSupportedException问题
问题根源
你遇到的错误核心原因是:你调用的LogError是静态扩展方法,而非ILogger<T>接口的原生实例方法。
ILogger<T>接口本身只定义了Log()这一个实例方法,所有LogError、LogInformation等常用日志方法,都是Microsoft.Extensions.Logging.LoggerExtensions类提供的静态扩展方法。Moq只能Mock/验证接口的原生实例成员,无法处理静态扩展方法,因此会抛出"方法不属于被Mock对象"或"非虚拟成员验证无效"的错误。
解决方案
直接针对ILogger<T>的原生Log()方法进行验证,因为所有扩展方法最终都会调用这个底层方法。
修改后的测试代码如下:
[Fact] public void Log_error_should_call_logger_error_method() { // Arrange var applicationException = new ApplicationException("This is a mocked exception"); var exceptionHandlerFeatureMock = ErrorsControllerGenerator.GenerateExceptionHandlerFeatureMock(applicationException); var loggerMock = new Mock<ILogger<ErrorsController>>(); // 注意:这里要传入Mock的实例对象,而非Mock本身 var sut = ErrorsControllerGenerator.GenerateErrorsController(exceptionHandlerFeatureMock, loggerMock.Object); var testErrorMessage = "This is a test error"; // Act sut.LogError(testErrorMessage); // Assert loggerMock.Verify(logger => logger.Log( // 匹配Error级别 LogLevel.Error, // 事件ID可以是任意值 It.IsAny<EventId>(), // 匹配包含目标消息的日志内容(扩展方法会包装消息成结构化对象) It.Is<It.IsAnyType>((state, type) => state.ToString().Contains(testErrorMessage)), // 本次调用无异常,匹配任意值 It.IsAny<Exception>(), // 日志消息格式化委托,匹配任意值 It.IsAny<Func<It.IsAnyType, Exception, string>>()), // 验证调用次数 Times.Once); }
关键说明
- 为何验证
Log()方法:所有LogXxx扩展方法都会将调用转发给Log(),并传入对应的LogLevel、结构化状态等参数,验证这个方法就能确认日志是否被正确触发。 - 状态对象匹配逻辑:扩展方法会把日志消息包装成内部结构化对象,因此不能直接匹配字符串,需通过
It.IsAnyType结合ToString()检查是否包含目标消息。 - Mock实例传递:确保传给控制器的是
loggerMock.Object(Mock生成的接口实例),而非loggerMock本身,否则控制器无法正确注入ILogger实例。
内容的提问来源于stack exchange,提问作者Pablo52
相关产品推荐
相关产品推荐

