【问题标题】:Internal security checks, how to reach 100 % code coverage?内部安全检查,如何达到 100% 的代码覆盖率?
【发布时间】:2019-04-07 18:54:37
【问题描述】:

我有很多类似于以下的类:

public class Foo
{
    private readonly Ba _ba;
    
    private Foo(Ba ba)
    {
        if (ba is null) throw new ArgumentNullException(ba);
        
        _ba = ba;
    }
}

在其他类的内部,我将这个构造函数称为Foo,但由于这是意料之外的,所以在每个构造函数中调用ba 不是null

我为组合框架编写了很多测试方法,但由于上述代码 sn-p 中的异常是较新抛出的,因此我无法达到 100% 的代码覆盖率。

我看到以下替代方案:

  • 删除空检查:这适用于当前的项目实现,但每当我可能添加一个意外调用Foo(null) 时,调试会更加困难。
  • [ExcludeFromCodeCoverage] 装饰构造函数: 这适用于当前的Foo(Ba) 实现,但每当我可能更改实现时,构造函数中的新代码路径可能会出现,并且会意外错过测试。

你会如何解决这个难题?


注意事项

代码示例是用 C# 编写的,但问题可能涉及一般的单元测试/异常处理问题。

C# 8 可能会通过introducing non-nullable reference types 解决这个问题,但我正在寻找一个好的解决方案,直到它稳定发布。

【问题讨论】:

  • 将构造函数设为protected,然后继承测试项目中的类,并用这个继承的类运行null测试?
  • 简单权衡:你有不小心写new Foo(null)的先例吗?我很确定——没有。然后您提到您编写了很多测试,这意味着每当您将null 传递给构造函数时,某些测试将失败,堆栈跟踪指向接收null 的类而不是有效依赖项。基于上述删除空检查并享受100%的代码覆盖率和无条件构造函数;)
  • 为什么不写一个测试来调用这个构造函数并将 null 作为参数传递呢?由于这是一个公共课程,我看不出问题出在哪里。

标签: c# unit-testing testing exception code-coverage


【解决方案1】:

您错过了最重要的替代方案:不要将其视为实现 100% 代码覆盖率的理想目标。

严格来说,您的代码中的稳健性检查无法以合理的方式进行测试。这也将发生在代码的其他各个部分 - 它经常发生在 switch 语句中,其中明确涵盖了所有可能的情况,并添加了一个额外的默认情况,只是为了抛出异常或以其他方式处理这种“不可能”的情况。或者,想想添加到代码中的断言语句:由于断言永远不会失败,严格来说,您将永远无法覆盖隐藏在断言语句中的else 分支 - 您如何测试断言中的表达式是很好地实际检测到您想要的问题?

删除此类健壮性代码和断言不是一个好主意,因为它们还可以保护您免受未来更改的不良副作用。对于您展示的示例,从覆盖率分析中排除代码可能是可以接受的,但在我提到的大多数情况下,这不是一个好的选择。最后,您必须做出明智的决定(通过详细查看覆盖率报告,而不仅仅是总体百分比)真正需要覆盖代码的哪些语句/分支等,哪些不需要。

最后,请注意,高代码覆盖率并不一定表明您的测试套件具有高质量。如果您的测试套件能够检测到代码中可能存在的错误,那么您的测试套件就具有很高的质量。您可以拥有一个覆盖率 100% 且不会检测到任何潜在错误的测试套件。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-05-18
    • 1970-01-01
    • 2017-12-07
    • 1970-01-01
    • 1970-01-01
    • 2019-11-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多