当前位置: 首页 > 知识库问答 >
问题:

由一个错误和突变测试

邓令雪
2023-03-14

在为我最喜欢的突变测试框架(NinjaTurtles)编写“逐个关闭”突变测试程序的过程中,我编写了以下代码,以提供检查我的实现正确性的机会:

public int SumTo(int max)
{
    int sum = 0;
    for (var i = 1; i <= max; i++)
    {
        sum += i;
    }
    return sum;
}

现在,这看起来很简单,我没有想到,在尝试变异IL中的所有文本整数常量时会出现问题。毕竟,只有3个(0、1和)。

错!

很明显,在第一次运行时,它永远不会在这个特定的情况下工作。为什么?因为将代码更改为

public int SumTo(int max)
{
    int sum = 0;
    for (var i = 0; i <= max; i++)
    {
        sum += i;
    }
    return sum;
}

只给和加0(零),这显然没有效果。如果是多个集合,情况就不同了,但在本例中并非如此。

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

sum = max * (max + 1) / 2;

我可以很容易地失败突变,因为从任何一个常量中添加或减去1都会导致错误。(给定max

所以,这个问题就解决了。虽然它并没有达到我想要的突变测试的目的,那就是检查当我丢失了代码时会发生什么-实际上是一个无限循环。但这是另一个问题。

那么,我的问题是:有没有任何普通或非普通的情况,从0或1开始的循环可能会导致“一个突变”的测试失败,而无法以类似的方式重构(测试中的代码或测试中的代码)?(请举例说明)

注:当应用突变后测试套件通过时,突变测试失败。

更新:下面是一个不那么琐碎的例子,但仍然可以对测试进行重构,使其失败

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

    return sum;
}

如果您提供的测试输入是new[]{0,1,2,3,4,5,6,7,8,9},则将var i=0更改为var i=1时,针对此代码的突变测试将失败。但是将测试输入更改为new[]{9,8,7,6,5,4,3,2,1,0},突变测试将失败。因此成功的重构证明了测试。


共有3个答案

夏侯阳
2023-03-14

是的,有很多,假设我理解你的问题。

与您的案例类似的是:

public int MultiplyTo(int max)
{
    int product = 1;
    for (var i = 1; i <= max; i++)
    {
        product *= i;
    }
    return product;
}

在这里,如果它从0开始,结果将是0,但是如果它从1开始,结果应该是正确的。(虽然它不会区分1和2!)。

龚奇逸
2023-03-14
匿名用户

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

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

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

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

按索引递增的顺序遍历一个正整数数组,对于每个索引,计算其对作为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]);
}

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

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

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

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

通骁
2023-03-14

我认为对于这种特殊的方法,有两种选择。你要么承认它不适合突变测试,因为这种数学异常,要么尝试以一种使突变测试安全的方式编写它,要么通过重构到你给出的形式,要么以其他方式(可能是递归的?)。

您的问题实际上可以归结为:在现实生活中,我们是否关心元素0是否包含在循环操作中或从循环操作中排除,并且我们无法围绕该特定方面编写测试?我的本能是说不。

你这个微不足道的例子可能就是我在博客中写的关于NinjaTurtles的文章中提到的缺乏测试驱动能力的一个例子。这意味着在您尚未尽可能重构此方法的情况下。

 类似资料:
  • 我有以下代码,我正在尝试决定如何处理错误: 服务器发回一个GraphQL错误对象,并将其发回,使其出现在突变的块中。如果变异成功,组件的数据将使用参数更新,但我看不到处理错误的等价方法。 理想情况下,我希望更新块中的Apollo缓存,但在这一点上,我甚至不能向组件或状态注入道具。 最终的结果是在Register组件上设置一个prop/state,它显示了一个带有条件呈现的错误消息组件。一种方法可以

  • 我使用的是Prisma GraphqQL,我在where选择器中发现了一个突变:“您为User上的where选择器提供了一个无效的参数” 突变: 变量: 结果: 架构: 为什么这种突变不起作用? 额外信息 这个项目的完整代码在这里: Graphql游乐场在这里:

  • 我在服务器上使用GraphQL和mongoose。 当发生验证错误时,GraphQL突变发送状态代码为200的响应。在客户端,响应如下所示: 我想使用阿波罗客户端突变promise的功能访问验证错误。类似: 如何做到这一点?

  • 在程序运行过程中,总会遇到各种各样的错误。 有的错误是程序编写有问题造成的,比如本来应该输出整数结果输出了字符串,这种错误我们通常称之为bug,bug是必须修复的。 有的错误是用户输入造成的,比如让用户输入email地址,结果得到一个空字符串,这种错误可以通过检查用户输入来做相应的处理。 还有一类错误是完全无法在程序运行过程中预测的,比如写入文件的时候,磁盘满了,写不进去了,或者从网络抓取数据,网

  • 在程序运行过程中,总会遇到各种各样的错误。 有的错误是程序编写有问题造成的,比如本来应该输出整数结果输出了字符串,这种错误我们通常称之为bug,bug是必须修复的。 有的错误是用户输入造成的,比如让用户输入email地址,结果得到一个空字符串,这种错误可以通过检查用户输入来做相应的处理。 还有一类错误是完全无法在程序运行过程中预测的,比如写入文件的时候,磁盘满了,写不进去了,或者从网络抓取数据,网

  • 问题内容: 我正在尝试在带有Visual Studio 2012的Windows 8.1上使用angularJS创建HTML5 / JS / CSS3应用程序。我目前无法将参数发送到其他视图。 谷歌搜索时,我看到几个示例,当我在Windows 8应用程序中执行此操作并单击链接时,出现以下错误。 未安装任何应用程序以打开此类链接(不安全) 当我将代码放在A标签之间时,它会显示其ID。 app.js