这是一个非常简单的例子,在这里用C来说明一个微妙的错误,我不知道如何通过测试公开bug。
考虑:
#include <stdio.h>
int main()
{
int a;
int b;
int input;
printf("Enter 1 or 2: ");
scanf("%d", &input);
switch(input) {
case 1:
a = 10;
/* ERROR HERE, I FORGOT A BREAK! */
case 2:
b = 20;
break;
default:
printf("You didn't listen!\n");
return 1;
break;
}
if(input == 1) {
b = 30;
printf("%d, %d\n", a, b);
} else {
printf("%d\n",b);
}
return 0;
}
如代码中所述,break
缺失,因此当输入1时,它会落到案例2中。虽然1的输出不会反映这一点,因为它稍后会覆盖b
。因此,我们可以设计的所有测试,例如通过输入集合{1, 2, 10}
中的数字都可以得到正确的输出。
实际上,switch
内的分配可能非常昂贵,因此这个错误可能非常昂贵。但是,假设它是从第一天起以这种方式编写的,那么没有基准可以看出成本高于预期。
那么可以做些什么来清除这些错误呢?有没有办法设计测试用例以在生产软件中公开它?
修改
所以我想我并不完全清楚 - 我用C语言写了它来说明遇到的问题的类型,但实际上它并不是特定于C.我想要做的是代码进入我们从未打算进入的部分(在这种情况下,由于忘记了break
来说明这一点)。我的实际情况是一个拥有700,000行的Fortran代码,它进入了我们从未打算进入的分支,因为从语言的角度来看,if / switch设计很差,但可能非常昂贵。
是否可以设计测试或查看某些工具中的某些数据,这些数据会告诉我们它应该进入不应该的分支?我通过打印错误地抓住了“我不应该在这里!“在所有案例中,看到它被打印出来,除了随机查看和打印报表之外,还有一个更好的方法。
答案 0 :(得分:1)
您可以为switch语句定义编码约定,以便每个分支都会强制执行特殊状态。就像一个变量被赋予了案例的价值。例如:
switch (v) {
case 1:
vcheck = 1;
...
break;
case 2:
vcheck = 2;
...
break;
}
在测试用例中测试vcheck
。
除此之外,您可以使用执行MISRA rules验证的静态代码分析的工具 - 并将它们引入您的构建过程。他们会引起一些心思......: - )
最后,(我最喜欢的)你可以编写一个脚本来检查这些情况并再次发出警告。
答案 1 :(得分:1)
案例1的正确状态是b不会被设置。
检查b是否已设定。
如果您稍后设置b,则可能需要将代码分解为较小的段以进行测试,但这只是很好的模块化。
好像你在问“如何测试不可测试的代码?”。答案是,需要技巧和计划来编写可测试的代码,它不仅仅是事后的想法。
网上有很多东西可以帮助你编写可测试的代码:
答案 2 :(得分:1)
对于您的具体示例,根据定义,它绝不是错误/错误。在语言中需要达到贬低的可能性。如果您想禁止某些危险的语言功能,那么linters就可以了。
为了避免完全不可预见的错误,有一条规则:始终采用防御性编码并使用assert
s,只要你可以使用它们。
答案 3 :(得分:1)
为什么这是我要问的错误?使用任何类型的黑盒测试代码都可以。因此,如果唯一的要求是代码有效,那么就没有错误。
但代码存在缺陷。缺少break
会使代码更难以理解并且难以维护。
编码约定是关于代码应该如何看的规则。通过测试已编译的程序无法实现编码约定,必须在源代码上完成。
测试编码约定是通过代码检查(自动或手动)完成的。
修改强>
如果您担心性能问题,请使用检测工具查找“热点”。您会发现大部分执行时间可能仅花费在几个模块中。查看这些模块以及每次调用它们。您会发现只需要查看10-30 000行代码。由于审查范围有限,审查应该需要1-3周。
结论:在发现细微错误时,代码审查远远优于测试。
答案 4 :(得分:0)
如果input==1
并且您在输出中看到b = 30
,则表示出现问题。另外,请记住在else子句中,你应该在读取之前给b写一些东西。如果是default:
,(例如,input==100
),您最终可能会从某个位置读取而未正确设置。
此外,代码审查,如果你能负担得起,应该有助于找到这样的事情。