如何使用NSubstitute对结构化日志调用进行单元测试?
问题描述
原日志代码:
_logger.LogError(exception, $"Encountered {exception.GetType().Name}. Unable to verify user with id {user.UserId}");
对应的单元测试断言原本可正常运行:
var logger = Substitute.For<ILogger<SyncService>>(); // 其他Arrange、Act、Assert步骤 logger.Received(1).LogError(exception, "Encountered NullReferenceException. Unable to verify user with id 1");
因需求改为结构化日志后,日志代码更新为:
_logger.LogError(exception, "Encountered {exceptionType}. Unable to verify user with id {userId}", exception.GetType().Name, user.UserId);
修改断言后测试失败,报错显示内部在比较string实例与FormattedLogValues实例,但FormattedLogValues已不再对外公开。已知Moq有It.IsAnyType方法可忽略消息模板类型,询问NSubstitute是否有类似方案。
解决方案
NSubstitute提供两种可行方案处理该问题:
1. 匹配任意类型绕过类型校验
使用Arg.Any<object>()匹配消息模板参数,无需关心内部的FormattedLogValues类型,直接校验结构化参数即可:
logger.Received(1).LogError( exception, Arg.Any<object>(), Arg.Is<string>(x => x == "NullReferenceException"), Arg.Is<int>(x => x == 1) );
2. 精确验证消息模板与参数
如果需要同时验证消息模板的正确性,可直接传入模板字符串,搭配参数匹配校验结构化参数:
logger.Received(1).LogError( exception, "Encountered {exceptionType}. Unable to verify user with id {userId}", Arg.Is<string>(x => x == exception.GetType().Name), Arg.Is<int>(x => x == user.UserId) );
这种方式更严谨,既保证消息模板符合预期,也能验证结构化参数的准确性,NSubstitute会自动处理内部的类型包装,无需手动适配FormattedLogValues。
内容的提问来源于stack exchange,提问作者asten_ark
相关产品推荐
相关产品推荐

