【问题标题】:Why is my unit test failing for a switch statement?为什么我的单元测试因 switch 语句而失败?
【发布时间】:2020-03-23 04:31:11
【问题描述】:

您好,我正在尝试对以下内容进行单元测试并检查此 switch/case 语句是否有效:

DiagnosticsComponentFactory

private readonly IServiceProvider _serviceCollection;

public IComponent Create (string name)
{
    switch (name)
    {
        case BoardItemComponent.TypeName:
            return _serviceCollection.GetService<BoardItemComponent>();
        default:
            throw new NotSupportedException();
    }
}

我的BoardItemComponent 班级:

public class BoardItemComponent
{
    public const string TypeName = "name";

    public BoardItemComponent(IConfigurationRepository configurationRepository) : base(configurationRepository)
    {

    }
}

BoardItemComponent 派生自IComponent,并在我的 Startup.cs 文件中添加如下:

services.AddScoped<IComponent, BoardItemComponent>();

我的单元测试:

[Test]
public void GetComponent_GivenComponentFactory_ExpectBoardComponent()
{
    var serviceCollection = new ServiceCollection();
    ComponentFactoryRegistration.Register(serviceCollection);
    var factory = new DiagnosticsComponentFactory(serviceCollection.BuildServiceProvider());
    var component = factory.Create("name");
    Assert.IsNotNull(component);
}

当我调试我的单元测试时,componentnull,尽管它遵循了所有正确的步骤。它正确地进入 switch case 语句并识别出正确的 case,但是它仍然返回 null

根据我分享的内容(并且很抱歉,因为我知道这些代码 sn-ps 中的名称在没有上下文的情况下含糊不清),是否有任何明显的原因导致我的单元测试失败?我是 C# 单元测试的新手。

【问题讨论】:

  • 显然 _serviceCollection.GetService&lt;BoardItemComponent&gt;() 无论出于何种原因返回 null。
  • 可能的原因是BoardItemComponent没有注册服务
  • 好的,谢谢@AFriend - 我该如何解决这个问题?
  • 在代码的每一步都需要验证每个变量的值和每个返回值。避免每个代码行有超过 1 个操作。 var factory = new DiagnosticsComponentFactory(serviceCollection.BuildServiceProvider()) 是两个。添加大量临时变量来分配值。对于那里的调试消息或单步执行调试器中的步骤将使您到达某个地方。 |无需担心性能。 JiT 和编译器优化非常适合在发布版本中删除未充分利用的变量。
  • @DetectivePikachu 第一个 sn-p 来自 DiagnosticsComponentFactory。注意Create 方法

标签: c# .net unit-testing dependency-injection nunit


【解决方案1】:

根据您的 cmets,您指出该组件已注册为

services.AddScoped<IComponent, BoardItemComponent>();

提供者知道IComponent 接口,但要求实现BoardItemComponent

_serviceCollection.GetService<BoardItemComponent>();

如果提供者在明确请求时不知道如何解析BoardItemComponent,则上述内容将返回null

注册实现

services.AddScoped<BoardItemComponent>();

您还可以使用工厂委托将其与抽象关联

services.AddScoped<IComponent>(sp => sp.GetRequiredService<BoardItemComponent>());

现在应该能够相应地进行隔离测试

[Test]
public void GetComponent_GivenComponentFactory_ExpectBoardComponent() {
    //Arrange
    var serviceCollection = new ServiceCollection();
    serviceCollection.AddScoped<BoardItemComponent>();
    serviceCollection.AddScoped<IConfigurationRepository>(sp => Mock.Of<IConfigurationRepository>());

    var factory = new DiagnosticsComponentFactory(serviceCollection.BuildServiceProvider());

    //Act
    var component = factory.Create(BoardItemComponent.TypeName);

    //Assert
    Assert.IsNotNull(component);
}

现在表示未来可能会有更多的实施。

举个例子

services.AddScoped<BoardItemComponent>();
services.AddScoped<AnotherItemComponent>();

然后可以重构工厂

public class DiagnosticsComponentFactory {    
    private readonly IServiceProvider services;

    public DiagnosticsComponentFactory (IServiceProvider services) {
        this.services = services;
    }

    public IComponent Create (string name) {
        switch (name) {
            case BoardItemComponent.TypeName:
                return services.GetRequiredService<BoardItemComponent>();
            case AnotherItemComponent.TypeName:
                return services.GetRequiredService<AnotherItemComponent>();
            default:
                throw new NotSupportedException();
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-04-24
    • 1970-01-01
    • 1970-01-01
    • 2013-07-06
    • 1970-01-01
    • 1970-01-01
    • 2019-11-29
    • 1970-01-01
    相关资源
    最近更新 更多