【问题标题】:How do I set up this unit test to test some types explicitly?如何设置此单元测试以明确测试某些类型?
【发布时间】:2022-01-21 02:51:54
【问题描述】:

我的CompositionRoot 静态类有一个测试夹具,它几乎只是枚举我的 Autofac IContainer 的服务并尝试实例化它们。如果它可以实例化它们,那就是测试通过。目标是确保我不会忘记向我的 Autofac 容器注册新接口。

但是,某些已注册的类型在其构造函数中使用不打算或不能在 Autofac 中注册的类型。例如,我有一个在其构造函数中采用string 的类。我将这个类注入我的代码库的方式是:

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&lt;&gt;)?

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

【问题讨论】:

    标签: c# unit-testing nunit autofac


    【解决方案1】:

    你需要做的是:

    • 解析 types 而不是 services,这样您就可以排除您想要的类型。
    • 删除重复项(因此,如果您有两个注册的东西 .As&lt;IFoo&gt;() 您不会运行该测试两次)。
    • 排除 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&lt;T&gt;、Func&lt;T&gt; 或 Autofac 支持的任何其他“内置关系”
    • 注册为 lambda 的任何内容
    • 解析ILifetimeScope 或IComponentContext 后可能在构造函数内完成的服务位置

    ...等等。我的意思是,你可能明白了,因为你已经通过TypedService 进行过滤,但我想我会很明确。这是 Autofac 中没有内置此类功能的主要原因 - 人们会认为我们有能力检查所有注册,而这实际上是不可能的。

    【讨论】:

    • 我记得先按类型和Resolve() 进行操作,然后因为某些事情不起作用而切换到服务。我认为这是使用 lambda 注册的服务。这解决了这个问题。
    • 我将此标记为答案,因为它部分回答了我的问题并提供了其他有用的补充。对于其他好奇的人,我在这里有完整的解决方案:gist.github.com/rcdailey/15317b1e87954ab714af2ab295e0f1d6
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-08
    • 1970-01-01
    • 2016-02-25
    • 1970-01-01
    • 1970-01-01
    • 2016-03-29
    相关资源
    最近更新 更多