通过故意破坏代码来验证单元测试

时间:2016-09-20 12:20:47

标签: unit-testing

我想测试(证明)我的单元测试是否实际测试了所需的一切。具体如何检查我是否没有错过某些断言?

以此代码为例:

int AddPositives(int a, int b)
{
    if (a > 0 && b > 0)
        return a + b;
    return -1;
}

有人写了一个单元测试:

    [Test]
    public void TestAddPositives()
    {
        Assert.AreEqual(3, AddPositives(1, 2));

        AddPositives(0, 1);
    }

很明显,这里错过了一个断言,你可能会在代码审查中发现它。但你怎么会自动捕捉到这个?

那么有什么东西会破坏经过测试的代码来检测丢失的Asserts吗?检查字节码并更改常量并删除代码以检查是否可以在没有单元测试失败的情况下更改内容的东西。

2 个答案:

答案 0 :(得分:1)

有几种方法可以帮助避免您所描述的问题:

1)您提到的“破坏”代码的方法称为“突变测试”。:创建被测系统的“突变体”,并查看被系统检测到的突变体数。测试套件。突变体是对SUT的修改,例如,通过替换代码中的运算符:代码中的一个+可以由-*替换。但是,还有更多的可能性来创建突变体。英文维基百科上有一篇有关突变测试的文章。在那里您还可以找到许多参考,其中一些列出了支持突变测试的工具。

突变测试可以帮助您检测“无效”的测试用例,但前提是您有一些参考表明应该检测到哪些突变。

2)测试优先方法/测试驱动开发(TDD)也有助于避免您描述的问题:在测试优先方案中,在编写使测试成功的代码之前先编写测试。因此,编写测试后,由于新测试,测试套件应该会失败。

您的方案(即您忘记添加断言)将被检测到,因为在添加(尚未完成)测试之后,您的测试套件不会失败,而是会继续成功。

但是,在执行代码之后,通常会执行其他测试,例如,以解决边界情况。在这些情况下,代码已经存在,然后您必须暂时“破坏”它,以查看其他测试失败。

3)正如其他人已经指出的那样,覆盖率分析可以帮助您发现缺少覆盖代码特定部分的测试。覆盖范围有不同的类型,例如语句覆盖范围,分支覆盖范围等。但是,使用高质量的测试套件,通常会多次覆盖一段代码,以解决边界情况和其他感兴趣的情况。然后,可能仍然无法检测到遗漏一个测试用例。

总结,尽管所有这些方法都可以以某种方式为您提供帮助,但是它们都不是防弹的。评论也不是,因为评论者也错过了一些要点。但是,审核可能会带来其他好处,例如,改进测试集或测试代码的建议。

答案 1 :(得分:0)

一些代码覆盖工具(如NCrunch(优秀但不是免费的))将注释您的代码行以显示测试是否会触及它们。

在你给出的例子中,NCrunch会在“return -1”旁边显示一个小黑点。线。这表明现有的测试没有通过该行代码,因此未经测试。

然而,这并不完美,因为您仍然可以编写一个测试来触及该行代码而不断言它返回-1,所以您不能认为这只是因为您有100%的覆盖率而您已编写了所有有意义的试验。所以它可以告诉你,返回-1绝对不是单元测试但它不会告诉你你没有测试边界条件(例如检查a = 0时会发生什么)