如何设置此单元测试以显式测试某些类型?
How do I set up this unit test to test some types explicitly?
我有一个用于我的 CompositionRoot
静态 class 的测试夹具,它几乎只是枚举我的 Autofac IContainer
的服务并尝试实例化它们。如果它可以实例化它们,那就是测试通过。目标是确保我没有忘记向我的 Autofac 容器注册新接口。
但是,某些已注册的类型在其构造函数中使用了不打算或不能在 Autofac 中注册的类型。例如,我有一个 class 在其构造函数中采用 string
。我将这个 class 注入我的代码库的方式是:
Func<string, ITypeThatRequiresAFactory>
这是我的测试夹具(使用 NUnit3 + FluentAssertions):
[TestFixture]
[Parallelizable(ParallelScope.All)]
public class CompositionRootTest
{
private sealed class ConcreteTypeEnumerator : IEnumerable
{
private readonly IContainer _container;
public ConcreteTypeEnumerator()
{
_container = CompositionRoot.Setup();
}
public IEnumerator GetEnumerator()
{
return _container.ComponentRegistry.Registrations
.SelectMany(x => x.Services)
.OfType<TypedService>()
.GetEnumerator();
}
}
[TestCaseSource(typeof(ConcreteTypeEnumerator))]
public void Service_should_be_instantiable(Service service)
{
using var container = CompositionRoot.Setup();
container.Invoking(c => c.ResolveService(service))
.Should().NotThrow()
.And.NotBeNull();
}
}
测试 Service_should_be_instantiable
在尝试实例化实现 ITypeThatRequiresAFactory
的服务时会失败,因为它的构造函数中有 string
参数。
如何将我的测试夹具重构为:
- 表达明确测试类型的列表
- 哪些被排除在
Service_should_be_instantiable
测试的类型列表之外
- 并且必须编写一个手动测试用例来验证它们是否可以解决(最有可能使用特殊情况的复合类型,例如
Func<>
)?
目标是将 Autofac 的隐式关系类型用于自动工厂 (Func<>
) 以及以后可能的其他隐式关系(如 Lazy<>
),因此需要我显式注册手写的解决方案例如,工厂是一个非首发者。
您需要做的是:
- 解析 types 而不是 services 以便您可以排除所需的类型。
- 删除重复项(因此,如果您注册了两件事
.As<IFoo>()
,您将不会 运行 测试两次)。
- 排除
Autofac
类型(因为 ILifetimeScope
和 IComponentContext
是固有注册的,不应该成为您测试的一部分)。
LINQ 看起来像:
var exclude = new []
{
typeof(ThingToSkip),
typeof(OtherThingToSkip),
};
// Get all the registrations
// Pull out the services
// Only look at typed (reflection) services
// Get the actual type from the service
// Filter down to unique types
// Exclude the types you don't want to test
// Exclude the Autofac types
var typesToTest = container.ComponentRegistry.Registrations
.SelectMany(x => x.Services)
.OfType<TypedService>()
.Select(t => t.ServiceType)
.Distinct()
.Except(exclude)
.Where(t => !t.FullName.StartsWith("Autofac."));
您的解决行将更改为:
container.Resolve(type);
...因为您将根据类型而不是服务进行解析。
我认为这应该可以让您到达目的地。
请注意,这不会涵盖诸如...
- 开放泛型
IEnumerable<T>
、Func<T>
或 Autofac 支持的任何其他“built-in 关系”
- 任何注册为 lambda 的东西
- 解析
ILifetimeScope
或 IComponentContext
后可能在构造函数内完成的服务位置
...等等。我的意思是,您可能明白了,因为您已经在按 TypedService
进行过滤,但我想我会说清楚。这是 Autofac 没有内置类似功能的主要原因——人们会认为我们有能力检查所有注册,但这实际上是不可能的。
我有一个用于我的 CompositionRoot
静态 class 的测试夹具,它几乎只是枚举我的 Autofac IContainer
的服务并尝试实例化它们。如果它可以实例化它们,那就是测试通过。目标是确保我没有忘记向我的 Autofac 容器注册新接口。
但是,某些已注册的类型在其构造函数中使用了不打算或不能在 Autofac 中注册的类型。例如,我有一个 class 在其构造函数中采用 string
。我将这个 class 注入我的代码库的方式是:
Func<string, ITypeThatRequiresAFactory>
这是我的测试夹具(使用 NUnit3 + FluentAssertions):
[TestFixture]
[Parallelizable(ParallelScope.All)]
public class CompositionRootTest
{
private sealed class ConcreteTypeEnumerator : IEnumerable
{
private readonly IContainer _container;
public ConcreteTypeEnumerator()
{
_container = CompositionRoot.Setup();
}
public IEnumerator GetEnumerator()
{
return _container.ComponentRegistry.Registrations
.SelectMany(x => x.Services)
.OfType<TypedService>()
.GetEnumerator();
}
}
[TestCaseSource(typeof(ConcreteTypeEnumerator))]
public void Service_should_be_instantiable(Service service)
{
using var container = CompositionRoot.Setup();
container.Invoking(c => c.ResolveService(service))
.Should().NotThrow()
.And.NotBeNull();
}
}
测试 Service_should_be_instantiable
在尝试实例化实现 ITypeThatRequiresAFactory
的服务时会失败,因为它的构造函数中有 string
参数。
如何将我的测试夹具重构为:
- 表达明确测试类型的列表
- 哪些被排除在
Service_should_be_instantiable
测试的类型列表之外
- 并且必须编写一个手动测试用例来验证它们是否可以解决(最有可能使用特殊情况的复合类型,例如
Func<>
)?
目标是将 Autofac 的隐式关系类型用于自动工厂 (Func<>
) 以及以后可能的其他隐式关系(如 Lazy<>
),因此需要我显式注册手写的解决方案例如,工厂是一个非首发者。
您需要做的是:
- 解析 types 而不是 services 以便您可以排除所需的类型。
- 删除重复项(因此,如果您注册了两件事
.As<IFoo>()
,您将不会 运行 测试两次)。 - 排除
Autofac
类型(因为ILifetimeScope
和IComponentContext
是固有注册的,不应该成为您测试的一部分)。
LINQ 看起来像:
var exclude = new []
{
typeof(ThingToSkip),
typeof(OtherThingToSkip),
};
// Get all the registrations
// Pull out the services
// Only look at typed (reflection) services
// Get the actual type from the service
// Filter down to unique types
// Exclude the types you don't want to test
// Exclude the Autofac types
var typesToTest = container.ComponentRegistry.Registrations
.SelectMany(x => x.Services)
.OfType<TypedService>()
.Select(t => t.ServiceType)
.Distinct()
.Except(exclude)
.Where(t => !t.FullName.StartsWith("Autofac."));
您的解决行将更改为:
container.Resolve(type);
...因为您将根据类型而不是服务进行解析。
我认为这应该可以让您到达目的地。
请注意,这不会涵盖诸如...
- 开放泛型
IEnumerable<T>
、Func<T>
或 Autofac 支持的任何其他“built-in 关系”
- 任何注册为 lambda 的东西
- 解析
ILifetimeScope
或IComponentContext
后可能在构造函数内完成的服务位置
...等等。我的意思是,您可能明白了,因为您已经在按 TypedService
进行过滤,但我想我会说清楚。这是 Autofac 没有内置类似功能的主要原因——人们会认为我们有能力检查所有注册,但这实际上是不可能的。