即使我有100%的代码覆盖率,我的代码仍然可以包含哪些类型的错误?我正在寻找具体的例子或链接到这些错误的具体例子.
拥有100%的代码覆盖率并不像人们想象的那么好.考虑一个trival示例:
double Foo(double a, double b) { return a / b; }
即使是单个单元测试也会将此方法的代码覆盖率提高到100%,但上述单元测试不会告诉我们哪些代码正在运行,哪些代码没有.这可能是一个非常有效的编码,但没有测试边缘条件下(例如当b
是0.0
)单元测试是不确定的最好的.
代码覆盖率只告诉我们单元测试执行的是什么,而不是它是否正确执行.这是一个重要的区别.仅仅因为一行代码由单元测试执行,并不一定意味着该行代码按预期工作.
听听这个有趣的讨论.
代码覆盖率并不意味着您的代码在任何方面都没有错误.这是对您的测试用例覆盖源代码库的程度的估计.100%的代码覆盖率意味着每行代码都经过测试,但程序的每个状态肯定都没有.在这个领域正在进行研究,我认为它被称为有限状态建模,但它确实是一种试图探索程序的每个状态的蛮力方式.
做同样事情的更优雅的方式被称为抽象解释.MSR(微软研究院)已经发布了一些基于抽象解释的CodeContracts.查看Pex,他们真正强调切割器测试应用程序运行时行为的方法.
我可以写一个非常好的测试,这将给我很好的报道,但不能保证该测试将探索我的程序可能具有的所有状态.这是编写非常好的测试的问题,这很难.
代码覆盖率并不意味着良好的测试
呃?我猜是什么样的普通逻辑错误?内存损坏,缓冲区溢出,普通旧错误代码,分配 - 而不是测试,列表继续.覆盖范围仅限于此,它可以让您知道所有代码路径都已执行,而不是它们是正确的.
由于我还没有看到它没有提到,我想添加这个线程代码覆盖它不会告诉你什么是你的代码的一部分,是bugfree.
它只告诉您保证代码的哪些部分未经测试.
1."数据空间"问题
你的(坏)代码:
void f(int n, int increment) { while(n < 500) { cout << n; n += increment; } }
你的考试:
f(200,100);
现实世界中使用的错误:
f(200,0);
我的观点:您的测试可能涵盖了代码的100%,但它不会(通常)覆盖所有可能的输入数据空间,即所有可能的输入值的集合.
2.根据自己的错误进行测试
另一个经典的例子是,你只是在设计中做出错误的决定,并根据自己糟糕的决定测试你的代码.
例如规格文件说"打印所有素数最多为n "并且您打印所有素数最多为n但不包括 n.你的单元测试测试你错误的想法.
3.未定义的行为
使用未初始化变量的值,导致无效的内存访问等,并且您的代码具有未定义的行为(使用C++或任何其他考虑"未定义行为"的语言).有时它会通过你的测试,但它会在现实世界中崩溃.
...