如何设置此单元测试以显式测试某些类型?

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 参数。

如何将我的测试夹具重构为:

目标是将 Autofac 的隐式关系类型用于自动工厂 (Func<>) 以及以后可能的其他隐式关系(如 Lazy<>),因此需要我显式注册手写的解决方案例如,工厂是一个非首发者。

您需要做的是:

  • 解析 types 而不是 services 以便您可以排除所需的类型。
  • 删除重复项(因此,如果您注册了两件事 .As<IFoo>(),您将不会 运行 测试两次)。
  • 排除 Autofac 类型(因为 ILifetimeScopeIComponentContext 是固有注册的,不应该成为您测试的一部分)。

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 的东西
  • 解析 ILifetimeScopeIComponentContext
  • 后可能在构造函数内完成的服务位置

...等等。我的意思是,您可能明白了,因为您已经在按 TypedService 进行过滤,但我想我会说清楚。这是 Autofac 没有内置类似功能的主要原因——人们会认为我们有能力检查所有注册,但这实际上是不可能的。