如何为细微错误设计软件测试

时间:2012-12-03 05:36:54

标签: c testing

这是一个非常简单的例子,在这里用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设计很差,但可能非常昂贵。

是否可以设计测试或查看某些工具中的某些数据,这些数据会告诉我们它应该进入不应该的分支?我通过打印错误地抓住了“我不应该在这里!“在所有案例中,看到它被打印出来,除了随机查看和打印报表之外,还有一个更好的方法。

5 个答案:

答案 0 :(得分:1)

您可以为switch语句定义编码约定,以便每个分支都会强制执行特殊状态。就像一个变量被赋予了案例的价值。例如:

switch (v) {
case 1: 
    vcheck = 1;
    ...
    break;
case 2:
    vcheck = 2;
    ...
    break;
}

在测试用例中测试vcheck

除此之外,您可以使用执行MISRA rules验证的静态代码分析的工具 - 并将它们引入您的构建过程。他们会引起一些心思......: - )

最后,(我最喜欢的)你可以编写一个脚本来检查这些情况并再次发出警告。

答案 1 :(得分:1)

案例1的正确状态是b不会被设置。

检查b是否已设定。

如果您稍后设置b,则可能需要将代码分解为较小的段以进行测试,但这只是很好的模块化。

好像你在问“如何测试不可测试的代码?”。答案是,需要技巧和计划来编写可测试的代码,它不仅仅是事后的想法。

网上有很多东西可以帮助你编写可测试的代码:

https://www.google.com/search?q=writing+testable+code

答案 2 :(得分:1)

对于您的具体示例,根据定义,它绝不是错误/错误。在语言中需要达到贬低的可能性。如果您想禁止某些危险的语言功能,那么linters就可以了。

为了避免完全不可预见的错误,有一条规则:始终采用防御性编码并使用assert s,只要你可以使用它们。

答案 3 :(得分:1)

为什么这是我要问的错误?使用任何类型的黑盒测试代码都可以。因此,如果唯一的要求是代码有效,那么就没有错误。

但代码存在缺陷。缺少break会使代码更难以理解并且难以维护。

编码约定是关于代码应该如何看的规则。通过测试已编译的程序无法实现编码约定,必须在源代码上完成。

测试编码约定是通过代码检查(自动或手动)完成的。

修改

如果您担心性能问题,请使用检测工具查找“热点”。您会发现大部分执行时间可能仅花费在几个模块中。查看这些模块以及每次调用它们。您会发现只需要查看10-30 000行代码。由于审查范围有限,审查应该需要1-3周。

结论:在发现细微错误时,代码审查远远优于测试。

答案 4 :(得分:0)

如果input==1并且您在输出中看到b = 30,则表示出现问题。另外,请记住在else子句中,你应该在读取之前给b写一些东西。如果是default:,(例如,input==100),您最终可能会从某个位置读取而未正确设置。

此外,代码审查,如果你能负担得起,应该有助于找到这样的事情。