为什么 AutoFixture return 在服务类型为对象时不注入服务(即使它们具有实际类型)?
Why doesn't AutoFixture return injected services when the services are of type object (even though they have an actual type)?
由于我尝试将 AutoFixture 与 BUnit 集成到 运行 某些 Blazor 测试的方式,我需要创建一个 IServiceProvider
将具有内部 Fixture
,当 AutoFixture 被请求创建该类型的对象时,我可以将对象注入到该夹具中以返回相同的对象。
我的IServiceProvider
看起来像这样
public class MockServiceProvider : IServiceProvider
{
private readonly Fixture fixture;
public MockServiceProvider()
{
this.fixture = new Fixture();
this.fixture.Customize(new AutoMoqCustomization { ConfigureMembers = true });
}
public void RegisterServices(params object[] serviceInstances)
{
foreach (object serviceInstance in serviceInstances)
{
this.fixture.Inject(serviceInstance);
}
}
public object GetService(Type serviceType)
{
return this.fixture.Create(serviceType, new SpecimenContext(this.fixture));
}
}
然后我这样注册我的服务实例:
[Theory, AutoDomainData]
public void TestSomething(
IMyFirstService firstService,
IMySecondService secondService,
MockServiceProvider serviceProvider)
{
serviceProvider.RegisterServices(firstService, secondService);
// test stuff
}
问题是任何时候 GetService
被调用时,fixture returns 一个新的服务实例而不是我注入的那个。
我认为问题是因为我将我的服务实例作为 object
传递给 RegisterServices
,AutoFixture 没有将它们注册为它们实际的类型。
我可以像这样一次注册一个服务实例来解决这个问题...
public void RegisterService<T>(T serviceInstance)
{
this.fixture.Inject(serviceInstance);
}
...但我很好奇为什么第一种方法不起作用。即使参数是一个 object
数组,AutoFixture 不应该仍然知道它们的实际类型吗?我也尝试过使用 RegisterServices(params dynamic[] serviceInstances)
,但这也不起作用。
之所以Inject<T>
不推断实例的类型而是依赖泛型参数是因为它需要启用需要为基类型或接口注册值的场景fixture.Inject<AbstractBaseType>(instance)
.
Inject
在幕后的工作方式是将注册委托给 Register<T>(Func<T> creator)
,然后再委托给 fixture.Customize<T>(c => c.FromFactory(creator).OmitAutoProperties())
。所以你可以看到当你需要类型推断时它是如何不起作用的。
你可以按照你说的一个一个注册,也可以自己实现注入。请注意,下面的实现是构建器的较轻实现,通常由 AutoFixture 生成,应该涵盖大多数场景。如果您发现某些功能不起作用,例如种子请求,您可能需要更完整的注入实现。
public static void InjectAsReflectedType(this IFixture fixture, object instance)
{
var builder = new FilteringSpecimenBuilder(
new FixedBuilder(instance),
new ExactTypeSpecification(instance.GetType()));
fixture.Customizations.Insert(0, builder);
}
或者,您始终可以通过包装器非泛型方法调用 Inject
方法,该方法通过反射调用目标方法。
由于我尝试将 AutoFixture 与 BUnit 集成到 运行 某些 Blazor 测试的方式,我需要创建一个 IServiceProvider
将具有内部 Fixture
,当 AutoFixture 被请求创建该类型的对象时,我可以将对象注入到该夹具中以返回相同的对象。
我的IServiceProvider
看起来像这样
public class MockServiceProvider : IServiceProvider
{
private readonly Fixture fixture;
public MockServiceProvider()
{
this.fixture = new Fixture();
this.fixture.Customize(new AutoMoqCustomization { ConfigureMembers = true });
}
public void RegisterServices(params object[] serviceInstances)
{
foreach (object serviceInstance in serviceInstances)
{
this.fixture.Inject(serviceInstance);
}
}
public object GetService(Type serviceType)
{
return this.fixture.Create(serviceType, new SpecimenContext(this.fixture));
}
}
然后我这样注册我的服务实例:
[Theory, AutoDomainData]
public void TestSomething(
IMyFirstService firstService,
IMySecondService secondService,
MockServiceProvider serviceProvider)
{
serviceProvider.RegisterServices(firstService, secondService);
// test stuff
}
问题是任何时候 GetService
被调用时,fixture returns 一个新的服务实例而不是我注入的那个。
我认为问题是因为我将我的服务实例作为 object
传递给 RegisterServices
,AutoFixture 没有将它们注册为它们实际的类型。
我可以像这样一次注册一个服务实例来解决这个问题...
public void RegisterService<T>(T serviceInstance)
{
this.fixture.Inject(serviceInstance);
}
...但我很好奇为什么第一种方法不起作用。即使参数是一个 object
数组,AutoFixture 不应该仍然知道它们的实际类型吗?我也尝试过使用 RegisterServices(params dynamic[] serviceInstances)
,但这也不起作用。
之所以Inject<T>
不推断实例的类型而是依赖泛型参数是因为它需要启用需要为基类型或接口注册值的场景fixture.Inject<AbstractBaseType>(instance)
.
Inject
在幕后的工作方式是将注册委托给 Register<T>(Func<T> creator)
,然后再委托给 fixture.Customize<T>(c => c.FromFactory(creator).OmitAutoProperties())
。所以你可以看到当你需要类型推断时它是如何不起作用的。
你可以按照你说的一个一个注册,也可以自己实现注入。请注意,下面的实现是构建器的较轻实现,通常由 AutoFixture 生成,应该涵盖大多数场景。如果您发现某些功能不起作用,例如种子请求,您可能需要更完整的注入实现。
public static void InjectAsReflectedType(this IFixture fixture, object instance)
{
var builder = new FilteringSpecimenBuilder(
new FixedBuilder(instance),
new ExactTypeSpecification(instance.GetType()));
fixture.Customizations.Insert(0, builder);
}
或者,您始终可以通过包装器非泛型方法调用 Inject
方法,该方法通过反射调用目标方法。