通过突变测试提高测试用例质量

文章:FrankBüchner

本文介绍了突变检测如何应用,可以提高您的测试用例的质量;由突变检测提出,他们可以如何克服的问题。

突变测试对测试用例的质量进行评估。它重新执行已经通过的测试用例,但是在更改的测试对象上,并揭示测试用例是否检测到测试对象中的更改。开发安全关键系统的标准,如IEC 61508,建议进行突变检测。在实践中,测试执行和突变生成的自动化是不可避免的。的新主版本V4.3中,自动化突变测试是最重要的新功能TESSY用于嵌入式软件的自动化单元,模块和集成测试工具。本文介绍了突变检测如何应用,可以提高您的测试用例的质量;由突变检测提出,他们可以如何克服的问题。

突变检测基础

突变检测重复的已经被通过了测试对象测试用例的执行,例如一个软件单元。但是,当反复进行试验的情况下,它们不与测试对象的原始源代码执行,但与已改变的代码(“突变”)。突变的代码是从原来的代码不同;的变化可涉及小的细节,如更换逻辑AND与逻辑OR;然而,这些变化也可以是剧烈的,例如去掉一如果指令的else分支。当然,测试对象必须甚至改变后仍然编译,否则测试重复是不可能的。

当重复试验用突变源代码的问题是,现有的测试案例是否揭示了突变(技术术语是“杀”)。如果在重复试验中的至少一个测试用例失败的突变被杀害。如果不这样做,测试用例不检测的源代码已被更改,或者换句话说:测试用例也考虑比原为正确其他测试对象。这是令人担忧的,需要进一步研究。对于这个调查,如果只有一个单一的突变已经取得了它是有帮助的。

如果幸存的突变不是等价的突变,那么测试用例的质量就会下降。等价的突变不会将测试对象的行为改变到外部,因此不能终止。下面给出一个等价变异的例子。当然,突变的剧烈程度很重要。一个细微的变异比几个剧烈的变异更难被发现。通常会执行几次带有不同突变的测试。这将评估测试用例的质量。

图1显示了TESSY自动进行突变测试的过程。在原始源代码通过所有现有测试之后,可以启动实际的突变测试过程。TESSY只执行一个突变,并重复所有现有的测试,当然记录突变是否被杀死。然后恢复原来的测试对象并执行另一个突变。


图1:TESSY自动化了整个突变测试过程。(来源:的Hitex)

图2显示了自V4.3版本以来TESSY可以执行的更改。用户可以选择要应用的突变,因此当然也会影响所执行的突变数量,而这反过来又会影响整个突变测试过程的执行时间。


图2:通过TESSY V4.3进行的默认突变。(来源:的Hitex)

两个假设的突变检测

默认情况下,TESSY执行的突变是微妙的,例如关系操作符' < '变成' <= '。这是基于“有能力的程序员”(Liggesmeyer)的假设,他认为一个有能力的软件开发人员只会犯小错误——例如,使用一个循环过多或过少(off-by-one错误)。为了查明测试用例是否发现了这样的小错误并因此具有良好的质量,突变必须是微妙的。根本的突变,例如删除一个甚至几个指令,也应该在低质量的测试用例中显示出来。另一个经经验证实的假设[offout]指出存在耦合效应:一个测试用例杀死一个突变体也杀死多个突变体。因此,一次只执行一个突变就足够了。

例子

我们认为一个测试对象已经通过了四个测试用例(图3),并且用这些测试用例实现了100%的代码覆盖率。


图3:该通应用于原始测试对象中的四个测试例测试数据。(来源:的Hitex)

如果TESSY进行突变测试(突变的标准设置如图2所示),结果是一个被杀死的突变体和一个幸存的突变体(图4)。


图4:TESSY突变检测结果。(来源:的Hitex)

在上图的左上部分(图4)中,示出了杀死的突变(“突变导致试验失败”)。此突变在从“<”到“<=”中的测试对象的第一个IF-指令中更改了关系运算符(在图4的右上角突出显示)。结果,测试用例失败,在突变测试中是正的。因此,这种突变标有绿色蜱虫。

在图4的左下部分,您可以看到存活的突变(“突变存活所有测试用例”);此突变在测试对象的第二次指令(在图4的右下角突出显示)从“>”到'> ='更改了关系运算符。没有测试用例通过失败检测到这一变化。这是值得怀疑的,需要调查。

突变得分和测试案例质量

突变比分是杀死突变的突变所有的比例。


图5:TESSY中的突变得分。(来源:的Hitex)

上面的图(图5)显示了TESSY为四个测试用例确定的突变得分(再次参考四个测试用例的测试数据,编号为1.1到4.1,在前面的图3中显示)。

测试用例2杀死了两个突变体中的一个,因为由于第一个IF-指令(V1

测试用例2杀死了一个突变体,因此比其他没有杀死任何突变体的测试用例具有更高的质量。这是由于测试用例2中的变量v1的值。这取决于第一个if指令中的关系操作符。在第二个测试用例中,变量v1和变量r1。Range_start的值是5,因此第一个if指令中的判断是' 5 < 5 ',计算结果为" false "。在突变中,决定是' 5 <= 5 ',计算结果为" true "。因此,第二个测试用例交付了一个意想不到的结果(“no”而不是正确和预期的“yes”),这将杀死突变体。

测试用例4应该在第二个if指令(v1 > r1.)的决定中杀死另一个突变(从' > '到' >= ')。range_start + r1.range_len)。但这不起作用,因为在测试用例4中v1的值不合适。变量v1的值是9和r1。range_start + r1。Range_len的结果是5 + 2 = 7。因此,第二个if语句中的决策在原始的' 9 > 7 '和突变的' 9 >= 7 '中,两者的计算结果都是" true "。因此,在这两种情况下,原始和突变体都给出正确的结果“no”;原始和突变都通过了第四个测试用例;这意味着突变体没有被杀死。

测试用例2比测试用例4有更好的质量,因为测试用例2使用边界值,而测试用例4没有。测试用例2使用边界值5,它是范围的起始值,起始值为5,长度为2。变量v1的值为9,测试用例4不使用range的边界值。

这说明了为什么边界值可以形成良好的测试数据,以及为什么安全关键软件开发标准推荐边界值作为测试数据。例如,IEC 61508[61508]在第3部分的表B.2和B.3中推荐了“边界值分析”方法。在这两个表中,这种方法被推荐用于SIL 1,并且强烈推荐用于SIL 2到4。ISO 26262[26262]也在第6部分“边界值分析”的表8中提到了方法1c,作为软件单元测试获取测试数据的一种程序。这种方法推荐用于ASIL A,强烈推荐用于ASIL B至D。

突变测试还可以评估测试用例集。如果一组测试用例杀死了所有的突变体,那么它就是足够的。适当的测试用例集越小越好。它也可以用来评估测试用例构建方法。

无尽的循环和崩溃

突变也会导致无尽的循环;这意味着测试没有结束。为了确保这样的变化不会使整个过程停止,TESSY监视执行时间。如果一个突变的执行时间超过了没有突变的执行时间的十倍,TESSY将终止测试执行。无限循环或超时杀死突变体。突变还可能导致测试对象崩溃,例如由于被零除。突变测试对象的崩溃也会杀死突变体。在超时或崩溃之后,如果适用更多的突变,则继续突变测试过程。


图6:一个无端环杀死突变体。(来源:的Hitex)

在上面的示例(图6)中,使用一个测试用例测试count()函数,该测试用例的参数x的输入值为10,并通过返回值1传递正确的结果。这个测试用例消除了图6左边所描述的所有四个突变。第三个突变(从' <= '到' >= ')不会像往常一样因为测试用例失败而终止,而是因为一个无限循环和由它触发的超时。TESSY认为这种突变被杀死了。之后,进行第四次突变。


图7:崩溃杀死突变体。(来源:的Hitex)

在上面的示例(图7)中,用一个测试用例对crash_by_divide()函数进行了测试,其中两个参数都在测试用例中一个b具有相同的价值。此测试用例在IF-yight的决定中杀死“!=”到'=='的突变。

等价突变是有问题的

突变检测的主要问题是等效突变。这些突变不会改变测试对象的外部行为。


图8:等效突变的示例。(来源:的Hitex)

另外,在上述(图8)图中的等效突变被示出。关系比较运算符从突变“>”到“> =”不具有外部可见的作用,因此不能用任何测试用例被杀死。但输入值0肯定导致的原始和突变的源代码不同的内部程序的行为。在原代码执行如果指令的其他分支,突变执行当时的分支。

因为等价的突变不能被测试用例杀死,所以必须手动(由人)检查所有幸存的突变,以确定它是否是等价的突变。这可能很耗时。然而,如果像TESSY一样,一次只发生一个突变,那么在这里它是有帮助的。此外,手边的测试对象是软件单元,相对于整个软件来说是很小的。这减少了检查等效突变的工作量。在它之上,我们可以假设,经过单元测试的安全关键软件,比其他软件有更好的测试用例,因为这个软件需要达到很高的代码覆盖率。这意味着,安全关键软件中只有一小部分(如果有的话)没有被任何测试用例执行。另一方面,没有像安全关键型软件那样彻底测试的软件可能有大量的代码没有被任何测试用例执行。很明显,没有被任何测试用例执行的软件部分的突变是不能被杀死的。这意味着有更多幸存的突变体,因此需要付出更大的努力在不充分的测试用例和等价的突变体之间做出决定。

等效突变可以看作是死亡突变;它们并不表明低质量的测试用例。

避免不必要的突变

在单位的安全关键软件测试,幸存突变(这是不等价突变)应导致改变或额外的测试用例。由于软件所需的高完整性,最终目标是使所有应用的突变被杀害(再次,不等同突变)。这可能不是进行集成测试的目标。集成测试的主要目的是测试设备的正确交互。因此,测试案例进行集成测试检查单元的相互作用,而不是如果正确各单个单元,以进行反应,可能是可能的每个错误条件(例如一个意外的NULL指针)。

在技​​术上,在整合测试期间引发错误条件可能是困难的,因此可能被忽视。支持这一点,因为可以假设在单元测试期间测试对误差条件的反应。因此,在集成测试期间达到100%代码覆盖率至关重要,尤其是代表防御性编程的代码部分(例如,对意外的空指针的反应)可能会被揭示。很明显,无法被任何测试用例执行的代码中的突变不能被杀死。应用这种突变导致人类努力进行手动调查这种突变,因为如果这种突变在幸存下来是不明显的,因为它是等效突变或由于低质量的测试用例。此外,施加这种突变增加了突变测试的执行时间。

如果代码覆盖信息在TESSY中对于突变测试是可用的,TESSY会避免代码中没有被任何测试用例执行的部分发生突变。这个特性在集成测试期间特别有用,因为可能有很大一部分未被发现的代码不会被任何测试用例执行。尽管用处不大,但是在单元测试期间,TESSY也可以抑制未覆盖代码中的突变。


图9:两个突变被抑制,因为它们不能被杀死。(来源:的Hitex)

在上图中(图9)的功能的推()和抽象数据类型“栈”中测试集成的弹出()。的图9推的源代码(的右侧)被显示。在第一线如果指令如果15个检查堆栈指针(可变next_free_element)已达到堆栈的顶部,指示堆栈溢出。第一,如果指令的则一部分被遮蔽在红色,指示它不是由任何测试情况下执行。结果,在第二个决策突变,​​如果指令(第17行)是检测不到的,并会生存下来。

Leveraging code coverage information, TESSY suppresses two mutations of the relational operator ‘>’ in the decision of the second if-instruction (error_report_level > 0), shaded in grey on the right-hand side of Figure 9. On the left-hand side of Figure 9, the same decision is shaded in grey and below of it, the two possible mutations are displayed (from ‘>’ to ‘<’ and from ‘>’ to ‘>=’). Both mutations were not applied. This is indicated by the dash (‘-‘) in the column “Result”.

如果TESSY执行突变测试而不先前代码覆盖测量,则TESSY应用第二个IF-指令的决定的两个突变。当然,他们都没有被杀。与单元测试相反,集成测试可能没有必要添加测试案例以检查是否正确处理了错误条件(我们的情况下的堆栈溢出)。通过避免这些突变,TESSY可以节省大量的时间,两者都可以通过计算机计算。

标准中的突变测试

IEC 61508将突变测试描述为“测试用例执行从错误播种”,建议这个安全完整性等级(SIL) 2 - 4表B.2(第3部分)。IEC 61508还规定(C.5.6节第7部分),可以估计错误的总数从一个测试套件的错误发现的数量在一个原始的测试对象以及这个测试套件杀死的突变数量(预测)。被杀死的突变体与突变体总数的比值等于在原始测试对象中发现的错误与原始测试对象中发现的错误总数的比值。这种估计自然假设了突变的类型和位置以及实际误差的相同统计分布;例如,如果实际误差是错误的计算,但没有使用算术变异,估计将很难准确。

ISO 26262在第6部分的表7中“故障注入测试”方法(方法1l)的注释中只提到了“代码突变”,它列出了软件单元验证的方法。

结论

突变测试可以揭示不充分的测试用例。改进它们增加了在测试软件中发现错误的机会。因此,突变测试不仅评估测试用例的质量,而且还有助于提高被测试软件的质量。在TESSY中,突变测试的执行是自动化的,因此执行不需要任何更大的努力。

然而,即使没有TESSY,每个处理测试项目的人都可以手动执行一些突变,并重新执行测试,看看测试用例是否杀死了突变。

术语

播种错误

这是IEC 61508对突变测试的称呼(参见第7部分C.5.6节)

耦合效果

如果用一个单一的突变的突变是由一组的测试案例发现,多个基因突变也被发现。

强突变试验

突变仅从外部考虑(黑盒),并且突变仅通过测试用例发现,测试用例在外部提供与原始结果不同的结果。

弱突变试验

测试用例确保突变体内部的行为与原始的行为不同。然而,这个其他行为在外部是不可见的。

充足的测试用例/足够的测试用例集

如果它杀死非等价突变的测试情况被称为充分。如果它杀死所有的非等价突变一组测试案例被称为充分。

突变分数

被杀死的突变体与突变体数量之比。通常以百分比表示。

故障注入

将外部错误注入到未变异的测试对象中,以测试鲁棒性。ISO 26262提到“代码突变”作为错误注入测试的例子。

参考

26262ISO 26262,国际标准,道路车辆-功能安全,第二版,2018
61508电气/电子/可编程电子安全相关系统的功能安全,第2版,2010。
LiggesmeyerLiggesmeyer, Peter: Software-Qualität:测试、分析和验证软件。德国,柏林,海德堡,2009年。Spektrum Akademischer -。
霍夫曼Dirk W. Hoffmann, Software-Qualität。施普林格-弗拉格柏林海德堡,2008年。
奥福特[李志刚,李志刚。软件测试耦合效应研究[j] .系统工程理论与实践,2004,(1)。

本文最初发布嵌入式

弗兰克·毕希纳在卡尔斯鲁厄技术大学学习计算机科学,现在是卡尔斯鲁厄理工学院(KIT)。毕业后,他在嵌入式系统领域的不同职位工作了30多年。多年来,他专注于嵌入式软件的测试和软件质量。目前他在Hitex GmbH是一家软件质量首席工程师。

发表评论