如何通过突变检测来提高测试案例的质量
扫描二维码
随时随地手机看文章
突变测试会对测试用例的质量进行评估。它在更改的测试对象上重新执行已经通过的测试用例,并显示测试用例是否检测到测试对象中的更改。发展安全关键系统的标准,例如:建议进行突变检测。在实践中,测试执行和突变生成的自动化是不可避免的。自动突变测试是新的主版本V4.3中最重要的新功能。TESSY 该工具用于嵌入式软件的自动化单元、模块和集成测试。本文展示了如何应用突变测试来提高测试案例的质量;突变测试引起的问题以及如何克服这些问题。
突变测试基本原理
突变测试重复执行测试对象已经通过的测试用例,例如软件单元。但是,当测试用例被重复时,它们不会使用测试对象的原始源代码来执行,而是使用已经更改的代码("变异")。变异的代码不同于原始代码;更改可能涉及较小的细节,例如替换逻辑和逻辑;然而,更改也可能是激烈的,例如删除IF指令的其他分支。当然,即使在更改之后,测试对象也必须保持可编译性,否则测试重复是不可能的。
当测试重复使用变异的源代码时,问题是现有的测试用例是否显示出突变(技术术语是"杀死")。在重复测试时,如果至少一个测试用例失败,突变就会被杀死。如果不发生这种情况,那么测试用例不会检测到源代码已经更改,或者换句话说:测试用例也会认为原来的测试对象以外的测试对象是正确的。这令人担忧,需要进一步调查。对于这个研究,如果只做了一个突变是有帮助的。
如果存活的突变不是一个等价的突变,那么测试案例就缺乏质量。对等的突变不会改变测试对象的外部行为,因此不能被杀死。下面给出了一个等价突变的例子。当然,这关系到突变有多激进。一个微妙的突变将比几个剧烈的变化更难以察觉。通常会进行几次不同突变的测试。它评估测试用例的质量。
图1显示了由TESSY自动进行的突变测试过程。在原始源代码通过所有现有测试之后,可以启动实际的突变测试过程。TESSY完全执行一个突变,重复所有现有的测试,当然记录一个突变是否被杀死。然后恢复原来的测试对象,进行另一个突变。
图1:TESSY自动化整个突变测试过程。
图2显示了自版本V4.3以来,TESSY能够实现的突变。用户可以选择应用的突变,因此当然也会影响所进行的突变的数量,这反过来又会影响整个突变测试过程的执行时间。
图2:TESSYV4.3执行的默认突变。
突变测试的两个假设
在默认情况下,TESSY执行的突变是微妙的,例如关系操作符"&t;"变成"<=="。这是基于"有能力的程序员"的假设,他说熟练的软件开发人员只会犯一些小的错误--比如说,使用一个循环来运行太多或者太少("一个接一个"的错误)。为了找出测试案例是否发现了这样的小错误,因此质量好,突变必须是微妙的。低质量的测试案例也应该显示激进的突变,比如去除一个甚至几个指令。另一个根据经验确认的假设(OOUT)指出,存在耦合效应:一个完全杀死一个突变体的测试案例也会杀死多个突变体。因此,每次只进行一次突变就足够了。
例子
我们考虑一个测试对象,它已经通过了四个测试用例(图3),并且在这些测试用例中实现了100%的代码覆盖。
图3:通过应用于原始测试对象的四个测试用例的测试数据。
如果TESSY进行了突变测试(对突变进行标准设置,如图2所示),结果是一个死亡的突变体和一个幸存的突变体(图4)。
图4:TESSY基因突变试验的结果。
在图的左上角(图4),显示了被杀死的突变("突变引起的测试失败")。这个突变改变了测试对象(图4右上角突出显示的)的第一个if指令中的关系操作符从"<"变为"<=="。结果,一个测试用例失败,在突变测试中呈阳性。因此,这种突变是用绿色的标记的。
在图4的左下角,您可以看到幸存的突变("突变在所有测试案例中幸存下来");这个突变改变了测试对象的第二个IF指令中的关系操作符(图4的右下角突出显示)从"&ttt;"变为"&ttt;="。没有测试用例通过失败检测到此更改。这一点值得怀疑,需要进行调查。
突变评分和测试用例质量
突变得分是所有突变与被杀突变的比率。
图5:TESSY的突变得分。
上面的图(图5)显示了TESSY为四个测试用例确定的突变评分(参见图3前面所示的四个测试用例的测试数据,编号为1.1至4.1)。
测试用例2杀死了两个突变体中的一个,因为测试用例2失败了,原因是第一个IF指令(v1&l;r1rrge_开始)从"&l;"<==开始)的突变。这就导致了50%的突变得分。测试用例2在M栏中标记绿色检查标记,因为它杀死了一个突变体。另外三个测试案例没有杀死任何突变人,因此有一个红十字或突变得分为0%。
测试案例2杀死了一个突变体,因此比没有杀死任何突变体的其他测试案例质量更高。这是因为测试用例2中变量V1的值。这取决于第一个if指令中的关系操作符。在第二个测试用例中,变量V1和变量R1范围_开始都有值5,因此第一个IF指令中的决定是'5<5',它的值是"假的"。在突变中,这个决定是"5<=5",它被评价为"真实"。因此,第二个测试用例提供了一个意想不到的结果("否",而不是正确的和预期的"是"),它杀死了突变体。
测试用例4应该在第二个IF-教授的决定中杀死另一个突变(从"&t;"到">=")。但是这并不起作用,因为测试用例4中的V1值是不合适的。变量V1的值为9和R1范围+R1范围=7。因此,第二个IF声明中的决定是在原来的"9&g;7"和突变体"9&g;=7"中作出的,两者都被评价为"真实"。因此,在两种情况下,原突变体和突变体都给出了正确的结果"不";原突变体和突变体都通过了第四个测试案例;这意味着突变体没有被杀死。
测试用例2比测试用例4有更好的质量,因为测试用例2使用一个边界值,而测试用例4没有。测试用例2使用边值5,它是从5开始的范围的起始值,长度为2。用变量V1的值9,测试用例4不使用范围的边界值。
这说明了为什么边界值是良好的测试数据,为什么开发安全关键软件的标准推荐边界值作为测试数据。例如,第3部分表B.2和B.3建议采用"边界值分析"方法。在这两个表格中,此方法被推荐为SL1,高度推荐为SL2至4。在第6部分"边界值分析"表8中,ISO26262[26262]还提到方法1c是获取软件单元测试测试数据的程序。该方法对AILA推荐,对B-D推荐较多。
突变测试也可以评估测试用例集。一组测试用例如果它能杀死所有的变种人,就叫做足够的。适当的测试用例设置越小越好.它也可以用来评估测试用例的施工方法。
无止境的循环和崩溃
突变也会导致无限循环;这意味着一个测试不会结束。为了确保这样的突变不会使整个过程陷入停顿,特塞监控执行时间。如果一个突变的执行时间超过了执行时间,而没有一个突变的次数增加了10倍,那么tesy将终止测试执行。无限循环或超时杀死突变体。突变也会导致测试对象崩溃,例如由于除法为0。突变测试对象的撞击也杀死了突变体。在超时或崩溃后,如果更多的突变是适用的,则继续进行突变测试过程。
图6:无限循环杀死突变体。
在上面的示例(图6)中,用一个测试用例测试计数()函数,该测试用例具有参数X的输入值10,并通过返回值1提供正确的结果。这个测试用例杀死了图6左侧所示的所有四个突变。第三种突变(从"&l;="到"&t;=")并非像往常一样被测试用例的失败所扼杀,而是被无止境的循环和它触发的超时所扼杀。TESSY认为这种突变是致命的。在此之后,进行了第四次突变。
图7:碰撞杀死了突变体。
在上面的例子(图7)中,用一个测试用例测试了崩溃_比_除法函数(),两个参数都在这个测试用例中。 A 和 b 有相同的价值。这个测试案例杀死了"!======='======'在-教授的决定中。
等量突变是有问题的
突变测试的主要问题是等效突变。这些突变不会改变测试对象的外部行为。
图8:等价突变的例子。
在上面的图(图8)中显示了一个等价的突变。关系比较运算符从"&984;"到"&ttt;="的突变没有一个外部可见的效果,因此不能被任何测试用例杀死。但是输入值0肯定会导致原始和变异源代码的不同内部程序行为。在原来的代码中,执行了I-教授的其他分支,突变执行了该分支。
由于对等突变不能通过测试用例来杀死,所以所有幸存的突变都必须通过手动(人类)检查,以确定它是否是对等突变。可能很耗时。然而,在这里,如果像TESSY一样,一次只产生一个突变,这是有帮助的。此外,手头的测试对象是软件单元,与整个软件相比,这些单元很小。这减少了检查对等突变的努力。除此之外,我们可以假设,进行单元测试的安全关键软件比其他软件有更好的测试用例,因为这种软件需要达到较高的代码覆盖率。这意味着,只有一小部分(如果有的话)的安全关键软件不会被任何测试用例执行。另一方面,没有像安全关键软件那样经过如此彻底测试的软件可能会有大量的代码,而这些代码并没有被任何测试用例执行。显而易见的是,软件中没有任何测试用例执行的部分的突变是不能被杀死的。这意味着存活的突变体数量增加,因此在测试案例不足和相当的突变体之间做出了更大的努力。
等值突变可以被看作是杀死的突变;它们并不表示低质量的测试案例。
避免不必要的突变
在安全关键软件的单元测试中,一个幸存的突变(这不是一个等价的突变)应该导致改变或增加测试用例。由于软件需要高度的完整性,最终目标是所有应用的突变都被扼杀(同样,不包括对等的突变)。这可能不是集成测试的目标。集成测试的主要目的是测试各单元之间正确的相互作用.因此,集成测试的测试用例检查单元的相互作用,而不是每个单元对每个错误条件作出正确反应(例如:一个意外的空指针)可能是可能的。
在集成测试中引发错误条件在技术上可能很困难,因此可能会被忽略。这是支持的,因为我们可以假设在单元测试期间测试了对错误条件的反应。因此,在集成测试中,达到100%的代码覆盖率并不是最重要的,特别是代码中代表防御性编程的部分(例如。对意外的空指针的反应可能会被发现。显而易见的是,任何测试用例都不执行的代码的突变是不能被杀死的。应用这种突变会导致人类为人工研究这种突变而付出的努力,因为这种突变是否能够幸存下来并不明显,因为它是一种等效的突变,或者因为测试案例质量低。此外,应用这种突变增加了突变测试的执行时间。
如果用于突变测试的TESSY可以提供代码覆盖信息,TESSY可以避免代码中任何测试用例都不执行的部分的突变。在集成测试期间,这个特性特别有用,因为潜在的大部分未发现的代码没有也永远不会被任何测试用例执行。虽然不那么有用,但在单元测试期间,TESSY也会抑制未发现代码中的突变。
图9:两个突变被抑制,因为它们不能被杀死。
在上图(图9)中,对抽象数据类型"栈"的函数推()和POP()进行了集成测试。图9的右侧显示了推()的源代码。第15行中的第一个if指令检查堆栈指针(变量Next_自由项)是否已到达堆栈顶部,表明堆栈溢出。当时的第一个if指令的一部分是红色的阴影,表示它没有被任何测试用例执行。因此,在第二行(在第17行中)的决定中的突变是无法检测到的,而且将会继续存在。
利用代码覆盖性信息,TISY在第二个IF指令的决策中抑制了关系操作符的两个突变"&ttt;"(错误的_报告;p;0),图9右侧的阴影是灰色的。在图9的左侧,同样的决定在灰色和下面的阴影下,显示了两种可能的突变(从'&pt;'到'<'和'&t;'到'&t;==')。两种突变都没有应用。这是"结果"一栏中的破折号(`-')所表示的。
如果TESSY在没有事先的代码覆盖度测量的情况下执行突变测试,TESSY将两个突变应用于第二个IF指令的决策。当然,他们都没死。与单元测试相反,集成测试可能不需要添加一个测试用例来检查是否正确地处理了错误条件(在我们的案例中堆栈溢出)。通过避免这些突变,TESSY为人类和计算机的计算节省了大量时间。
标准突变测试
IEC61508将突变测试描述为"从错误播种执行的测试案例",并建议将此描述为安全完整性等级2至4(见第3部分表B.2)。(第7部分C.5.6节)还指出,人们可以从测试套件在原始测试对象中发现的错误数和这个测试套件杀死的突变数(预测性)来估计错误总数。被杀突变体与突变体总数的比率等于在原始测试对象中发现的错误与原始测试对象中错误总数的比率。这种估计自然假设了相同的突变类型和位置的统计分布和实际的错误;例如,如果实际的错误是错误的计算,但没有使用算术突变,这个估计很难准确.
ISO26262在第6部分表7"故障注入测试"方法(方法1L)的说明中仅提到"代码突变",其中列出了软件单元的验证方法。
结论
突变试验可以揭示试验案例不足。改进它们增加了在测试软件中发现错误的机会。因此,突变测试不仅可以评估测试案例的质量,而且还可以有助于改进测试软件的质量。突变测试的执行是在TISY中自动化的,因此执行不需要任何更大的努力。
然而,即使不需要TESSY,每个处理测试项目的人都可以手动执行一些突变,并重新执行测试,看看测试用例是否能杀死突变。