关闭一个错误和变异测试

pms*_*969 7 c# algorithm il mutation-testing ninja-turtles

在为我最喜欢的突变测试框架(NinjaTurtles)编写"Off By One"变异测试程序的过程中,我编写了以下代码,以便提供检查我的实现的正确性的机会:

public int SumTo(int max)
{
    int sum = 0;
    for (var i = 1; i <= max; i++)
    {
        sum += i;
    }
    return sum;
}
Run Code Online (Sandbox Code Playgroud)

现在这看起来很简单了,并没有让我觉得尝试改变IL中的所有文字整数常量会有问题.毕竟,只有3(the 0,the 1,and ++).

错误!

在第一次运行中它变得非常明显,它在这个特定的实例中永远不会起作用.为什么?因为将代码更改为

public int SumTo(int max)
{
    int sum = 0;
    for (var i = 0; i <= max; i++)
    {
        sum += i;
    }
    return sum;
}
Run Code Online (Sandbox Code Playgroud)

只在总和上加0(零),这显然没有效果.不同的故事,如果它是多组,但在这种情况下,它不是.

现在有一个相当简单的算法来计算整数之和

sum = max * (max + 1) / 2;
Run Code Online (Sandbox Code Playgroud)

我可以轻易地使突变失败,因为从任一常数中加1或减1会导致错误.(鉴于此max >= 0)

因此,针对这种特殊情况解决了问题.虽然它没有做我想要的突变测试,这是为了检查当我丢失时会发生什么++- 实际上是一个无限循环.但这是另一个问题.

所以 - 我的问题: 是否有任何琐碎或非平凡的情况,从0或1开始的循环可能会导致"一个突变"的测试失败,无法以类似的方式重构(测试中的代码或测试)?(请举例)

注意:在应用突变后测试套件通过时,突变测试失败.

更新:一个不那么简单的事情的例子,但是仍然可以重新测试以使其失败的事情将是以下内容

public int SumArray(int[] array)
{
    int sum = 0;
    for (var i = 0; i < array.Length; i++)
    {
        sum += array[i];
    }

    return sum;
}
Run Code Online (Sandbox Code Playgroud)

如果您给出的测试输入是var i=0,var i=1则在更改to 时,针对此代码的变异测试将失败new[] {0,1,2,3,4,5,6,7,8,9}.但是,将测试输入更改为new[] {9,8,7,6,5,4,3,2,1,0},并且突变测试将失败.所以一个成功的重构证明了测试.

Evg*_*uev 3

“突变测试失败”的一种自然情况是矩阵转置算法。为了使其更适合单个 for 循环,请为此任务添加一些约束:让矩阵为非方形并要求转置就地。这些约束使得一维数组最适合存储矩阵,并且可以使用 for 循环(通常从索引“1”开始)来处理它。如果从索引“0”开始,则不会发生任何变化,因为矩阵的左上角元素始终转置为自身。

有关此类代码的示例,请参阅其他问题的答案(抱歉,不是在 C# 中)。

这里“变异关闭一”测试失败,重构测试不会改变它。我不知道代码本身是否可以重构以避免这种情况。理论上是可以的,但是应该太难了。


我之前引用的代码片段并不是一个完美的示例。如果将 for 循环替换为两个嵌套循环(就像行和列一样),然后将这些行和列重新计算回一维索引,则仍然可以重构它。尽管如此,它还是给出了如何制作一些无法重构的算法的想法(尽管不是很有意义)。

按索引递增的顺序迭代正整数数组,对于每个索引将其对计算为i + i % a[i],如果它不在界限之外,则交换这些元素:

for (var i = 1; i < a.Length; i++)
{
    var j = i + i % a[i];
    if (j < a.Length)
        Swap(a[i], a[j]);
}
Run Code Online (Sandbox Code Playgroud)

这里 a[0] 再次是“不可移动的”,重构测试不会改变这一点,并且重构代码本身实际上是不可能的。


又一个“有意义”的例子。让我们实现一个隐式二叉堆。它通常被放置到某个数组中,从索引“1”开始(与从索引“0”开始相比,这简化了许多二进制堆计算)。现在为此堆实现一个复制方法。此复制方法中的“相差一”问题无法检测到,因为未使用索引零,并且 C# 对所有数组进行零初始化。这和OP的数组求和类似,但是不能重构。

严格来说,你可以重构整个类,一切从“0”开始。但仅更改“复制”方法或测试并不能防止“突变关闭”测试失败。二叉堆类可以被视为复制具有未使用的第一个元素的数组的动机。

int[] dst = new int[src.Length];
for (var i = 1; i < src.Length; i++)
{
    dst[i] = src[i];
}
Run Code Online (Sandbox Code Playgroud)