【发布时间】:2025-12-15 05:00:02
【问题描述】:
我正在尝试对一段代码进行单元测试,该代码使用IServiceProvider 和反射的组合来创建扩展抽象类BaseCommand 的每个类的实例:
IEnumerable<BaseCommand> commandsInAssembly = typeof(BaseCommand)
.Assembly.GetTypes()
.Where(t => t.IsSubclassOf(typeof(BaseCommand)) && !t.IsAbstract)
.Select(t => (BaseCommand)ActivatorUtilities.CreateInstance(_serviceProvider, t))
.ToList();
这里的棘手部分是_serviceProvider 被注入并且需要被模拟(我认为),以允许这段代码成功且独立地运行。每个命令都需要访问 DI 以解决其依赖关系。大多数命令看起来类似于:
public SomeCommand(IAppState appState, ILoggerAdapter<SomeCommand> logger) : base(appState)
我能够很好地模拟 IServiceProvider 以解决 IAppState,但我在使用 ILoggerAdapter<> 时遇到了困难。这是我目前的设置:
单元测试构造函数
var serviceProvider = new Mock<IServiceProvider>();
serviceProvider
.Setup(x => x.GetService(typeof(IAppState)))
.Returns(new AppState());
serviceProvider
.Setup(x => x.GetService(typeof(ILoggerAdapter<>)))
.Returns(typeof(LoggerAdapter<>));
var serviceScope = new Mock<IServiceScope>();
serviceScope
.Setup(x => x.ServiceProvider)
.Returns(serviceProvider.Object);
var serviceScopeFactory = new Mock<IServiceScopeFactory>();
serviceScopeFactory
.Setup(x => x.CreateScope())
.Returns(serviceScope.Object);
serviceProvider
.Setup(x => x.GetService(typeof(IServiceScopeFactory)))
.Returns(serviceScopeFactory.Object); var mocker = new AutoMocker();
_commandDispatcher = new CommandDispatcher(serviceProvider.Object, _mockAppState.Object, _mockLogger.Object);
这会产生以下错误: System.InvalidOperationException:尝试激活“SomeCommand”时无法解析“ILoggerAdapter`1[SomeCommand]”类型的服务。
如果我尝试更明确地设置我的设置(我想避免这样做,这会使测试变得更加脆弱)并使用:
serviceProvider
.Setup(x => x.GetService(typeof(ILoggerAdapter<SomeCommand>)))
.Returns(typeof(LoggerAdapter<SomeCommand>));
但这也会产生错误:System.ArgumentException : 'System.RuntimeType' 类型的对象无法转换为 'ILoggerAdapter`1[SomeCommand]' 类型。
我了解到使用 AutoMocking 容器或 Fixture 可能更合适,但我不确定从哪里开始。我对 C# 中的单元测试相当陌生。
如何模拟/向我的 SUT 提供 IServiceProvider 而不会导致 ActivatorUtilities.CreateInstance(IServiceProvider, type) 爆炸?
【问题讨论】:
-
我认为,如果您觉得这太难测试,我建议您没有以最佳方式构建它
-
这是可能的。我希望这更多是我缺乏测试经验的问题。这感觉像是一个简单的起订量配置问题,我只是不太了解。
-
所以我不会太担心创建了正确的实例,更多的是你应该做
mock.Setup(foo => foo.CreateInstance(It.Is<SomeType>())).Returns(new MyMockType);然后做两次检查,首先是mock.Verify()以确保调用该方法,其次是检查实例的类型是 MyMockType
标签: c# unit-testing asp.net-core moq xunit