AutoFixture集成BUnit进行Blazor测试时,注入object类型服务无法返回已注册实例的问题
MockServiceProvider's RegisterServices Method Isn't Reusing Injected Instances 你推测的原因完全正确——问题出在AutoFixture.Inject方法的重载行为上,而非你传入实例的方式。让我们一步步拆解:
The Root Cause: AutoFixture's Inject Overloads
AutoFixture提供了两个关键的Inject重载:
- 泛型重载:
public void Inject<T>(T instance)
这个重载会把instance注册为**类型T**的唯一实例。当你请求T类型的对象时,AutoFixture会直接返回这个注入的实例。 - 非泛型重载:
public void Inject(object instance)
这个重载只会把instance注册为**类型object**的实例,而不是它的实际运行时类型。
在你的RegisterServices方法中,serviceInstance是object类型,所以编译器会绑定到非泛型的Inject(object)重载。这意味着你注入的实例只会被关联到object类型,而不会关联到它们实际实现的接口(比如IMyFirstService)。当你调用GetService(typeof(IMyFirstService))时,AutoFixture找不到针对IMyFirstService的注入实例,就会创建一个新的实例。
Why dynamic Doesn't Fix It
使用params dynamic[]也不会解决问题,因为C#的动态绑定在编译时仍然会选择最匹配的重载——这里还是会选中Inject(object),因为dynamic变量在编译阶段的静态类型还是object。
Solutions
1. Use the Generic RegisterService<T> Method (Your Current Fix)
你已经发现的泛型方法是最直接的解决方案,因为它明确调用了Inject<T>重载,把实例注册为正确的类型:
public void RegisterService<T>(T serviceInstance) { this.fixture.Inject(serviceInstance); }
2. Add a Batch Registration Method Using Reflection
如果你想保持批量注册的便利性,可以通过反射调用泛型的Inject方法,自动获取每个实例的实际类型:
public void RegisterServices(params object[] serviceInstances) { foreach (var serviceInstance in serviceInstances) { var injectMethod = typeof(Fixture).GetMethod(nameof(Fixture.Inject))! .MakeGenericMethod(serviceInstance.GetType()); injectMethod.Invoke(this.fixture, new[] { serviceInstance }); } }
这个方法会为每个实例找到对应的泛型Inject<T>重载,并调用它,确保实例被注册为其实际的运行时类型。
Testing the Fix
修改后,当你调用serviceProvider.RegisterServices(firstService, secondService),AutoFixture会将这些实例分别注册为IMyFirstService和IMySecondService类型。后续调用GetService请求这些类型时,就会返回你注入的实例,而不是创建新的。
内容的提问来源于stack exchange,提问作者Ben Rubin

