文章总结: 本文详细介绍了软件控制流用例设计的六种方法,从基础的语句测试到高级的修正条件判定覆盖(MC/DC)测试,分析了各种方法的优缺点和适用场景。特别强调了MC/DC测试在航空航天、轨道交通等高安全可靠性软件测试中的重要性,它能以较少用例实现高覆盖率。文章还结合EN50128标准,说明了不同安全等级软件的测试要求,指出当前行业测试中存在的误区,强调测试目的不仅是跑完代码,更是证明代码逻辑和数据行为可控。 综合评分: 85 文章分类: 代码审计,应用安全,安全建设,技术标准,解决方案
一文看懂软件控制流用例设计方法
原创
薄说安全
薄说安全
2025年12月22日 07:00 北京
在软件动态测试中,主要关注语句、分支、路径等程序结构的覆盖,如何设计较少的用例,达到更高的覆盖率。很多人以为“代码跑过了,就算测到了。”但在功能安全软件测试,这远远不够。本文对控制流用例设计的不同覆盖条件,做一个总结。
一、语句测试(Statement Testing)
语句测试的要求使被测程序中的每条语句都要被遍历到。语句测试是从每行代码有没有被执行过考虑,但对程序执行逻辑的覆盖很低,不关心代码事怎么走到这里的。
示例1
b = 0;if (a > 0){ b = 1;}c = 2;
构造以下测试用例:
输入:a=1输出:b=1;c=2
上面这一个用例中,所有语句都被执行过,语句覆盖100%,但对于a <= 0的情况完全没测,判断逻辑是否正确无法保证。因此,一般认为语句测试是很弱的逻辑覆盖。
二、分支测试(Branch Testing)
分支测试的要求是使得程序中的每个分支都要被遍历到,即使是分支上没有语句。
还以第一个示例来说,实现分支覆盖,需要设计两个用例:
| 用例编号 | 输入值 (a) | 判断结果 | 执行分支 | 预期结果 | | — | — | — | — | — | | TC1 | a = 5 | true | 进入if块 | b = 1, c = 2 | | TC2 | a = -3 | false | 跳过if块 | b = 0, c = 2 |
分支测试不关心分支中的判定条件是否都变化,如果分支的判定逻辑运算有问题,需要通过其它测试来弥补。
三、判定测试(Decision Testing)
判定测试的要求是使得程序中的每个判定语句的取值都要被覆盖到,对于真假双值的判定来说,取“真”和取“假”各一次;对于多值判定,如switch-case结构,要保证所有的case和default分支均要取到。
由于程序中不同分支都是基于判定语句的取值来划分,判定测试与分支测试密切相关,但判定测试100%并不等于分支测试100%,看下面的示例2:
if (sensor_ok && speed < SPEED_MAX){ allow_move();}else inhibit_move();
设计以下两个用例
用例 1:sensor_ok = truespeed = 10 → 判定为 True,走if分支
用例 2:sensor_ok = falsespeed = 10 → 判定为 False,走else分支
以上两个用例设计达到判定覆盖100%,但是未达到分支测试100%,下面这个分支条件没有被测试到。
sensor_ok = truespeed >= SPEED_MAX
因此,判定测试100%不能覆盖组合条件的情况。
4.分支条件测试(Branch Condition Testing)
基于判定语句的覆盖无法测试到组合条件判定的各个条件,对于覆盖所有判定条件的要求来设计用例,使得每个判定条件的取值都被满足,就是条件测试。在满足条件测试的前提下,每个判定条件也被覆盖到,就是分支条件覆盖。示例3
if (door_closed && speed < 5 && emergency_clear){ allow_move();}else{ inhibit_move();}
满足分支条件测试的最小用例集满足分支覆盖
- if 分支至少执行一次
- else 分支至少执行一次
条件覆盖中每个基本条件
- 至少一次为 True
- 至少一次为 False
因此最少需要4个用例为:
| 用例 | door_closed | speed < 5 | emergency_clear | 执行分支 | | — | — | — | — | — | | TC1 | T | T | T | if | | TC2 | F | T | T | else | | TC3 | T | F | T | else | | TC4 | T | T | F | else |
五、分支条件组合测试(Branch Condition Combination Testing)
分支条件组合测试是要求每个判定语句中的所有判定条件的各种可能组合都至少出现一次,因此,满足分支条件组合测试100%覆盖的用例集,其语句覆盖、分支测试、判定测试、分支条件测试的覆盖率也一定是100%。以上面的代码为例,有3个判定条件,满足分支条件组合测试,两两组合后需要设计8个用例:
| door_closed | speed < 5 | emergency_clear | | — | — | — | | T | T | T | | T | T | F | | T | F | T | | T | F | F | | F | T | T | | F | T | F | | F | F | T | | F | F | F |
因此,分支条件组合测试的覆盖会全部遍历所有分支和判定条件,所需的测试用例数也很庞大,对于包含n个布尔条件的代码,就需要2的N次幂个测试用例来实现100%覆盖率。这样的测试成本极高,实际项目中更多使用MC/DC能用更少用例达到更高测试覆盖。
六、修正条件判定覆盖(Modified Condition Decision Coverage Testing)
修正条件覆盖(MC/DC)测试理念最早提出是由航空航天DO-178B(现已升级到DO-178C),针对民航机载系统设备适航审定的软件测试要求。这种测试设计覆盖方法既保持了与分支条件组合测试相同的覆盖强度,又减少了测试成本,目前在轨道交通、汽车、工控各行业的高安全可靠性软件测试得到广泛应用。MC/DC测试的要求是设计足够的用例把各个条件能够影响到包含判定结果的都能被覆盖到。
还以示例3的代码为例,采用MC/DC测试方法,每个基本条件都要找到一对测试用例,能够只改变单个条件,其它条件保持不变,而判定结果发生变化。
最小用例集为
| 用例 | C1 door_closed | C2 speed<5 | C3 emergency_clear | 判定 D | 执行分支 | | — | — | — | — | — | — | | TC1 | T | T | T | T | if | | TC2 | F | T | T | F | else | | TC3 | T | F | T | F | else | | TC4 | T | T | F | F | else |
因此,对于3个布尔条件,最少需要4个测试用例实现100%覆盖率。扩展到包含n个布尔条件的代码,MC/DC测试只需要n+1个测试用例即可实现100%覆盖率。
需要注意的是MC/DC测试用例最小集并不总是与分支条件最小集合相同,如果判定条件变复杂,那么MC/DC会不同于分支条件测试的最小集。
示例4
bool door_closed; // A:车门关闭bool speed_ok; // B:速度满足启动条件bool emergency_release; // C:紧急制动无施加bool allow_move;
allow_move = (door_closed && speed_ok) || emergency_release;
if (allow_move){ allow_traction();}else{ inhibit_traction();}
对于条件A=door_closed, B = speed_ok, C = emergency_traction
判定 D = (A && B)|| C
采用分支条件测试方法,最小用例集为
| 用例 | A door_closed | B speed_ok | C emergency_release | D 结果 | 分支 | | — | — | — | — | — | — | | TC1 | T | F | T | T | if | | TC2 | F | T | T | T | if | | TC3 | T | T | F | T | if | | TC4 | F | F | F | F | else |
以上用例集,分支覆盖100%,条件覆盖100%,但不满足MC/DC,以上4条用例中,没有测试到只变A条件或B条件,D判定结果变化。满足MC/DC的最小用例集为
| 用例 | A | B | C | D | | — | — | — | — | — | | M1 | T | T | F | T | | M2 | F | T | F | F | | M3 | T | F | F | F | | M4 | F | T | T | T |
在以上用例中,能够证明条件A或B或C,分别独立影响D判定结果。因此,MC/DC的优势在于不仅覆盖所有条件和分支,还能够利用最小的测试用例集实现对每个条件对判定的独立影响,更有价值。
下面以轨道交通安全相关软件标准EN50128中对代码测试方法的要求,说明测试方法的应用。
在 EN 50128 Table A.21 中,明确列出的覆盖率准则是 5 类:
- Statement(语句)
- Branch(分支)
- Compound Condition(复合条件)
- Data Flow(数据流)
- Path(路径)
其中
第1类语句覆盖是SIL1~SIL4基本要求,会被2-5自动包含,更多是用于发现完全没跑到的死代码。
第2类分支覆盖是最低可接受的结构覆盖,能够发现走不到的else分支,在组合条件中被掩盖的控制流分支,但无法证明组合条件中每个条件的影响。
第3类组合条件覆盖,要求判定中每一个条件都至少取过真值和假值,EN50128没有强制要求MC/DC,但对于使用了复合逻辑条件,安全功能依赖于组合条件判定的情况下,从以上MC/DC测试方法的优势中,强烈推荐采用MC/DC来满足这一条。
第4类数据流覆盖,是验证变量从“定义(赋值)”到“使用”的路径是否被正确覆盖,是不同于控制流用例设计的另外一种方法,能够发现未初始化变量、变量定义后未使用、使用后未清除等数据使用问题。
第5类路径覆盖,是以执行路径为中心的测试方法,与组合条件覆盖相比,对所有可能执行路径设计测试用例。
EN 50128中对SIL3/SIL4级软件,推荐要求为:
2(分支覆盖)+3(组合条件覆盖)或;
2(分支覆盖)+4(数据流覆盖)或;
5(路径覆盖)。
其中在实际工程中较为常用测试方法组合为2(分支覆盖)+3(组合条件覆盖),数据流在涉及到变量跨函数、跨周期存在,直接影响安全输出的情况下,也需要考虑进行数据流测试。
目前轨道交通行业软件单元存在很多误区,审核中经常能够看到,片面追求覆盖率高,但没关注哪些路径真的跑过,哪些条件真正起作用;测试人员不理解代码,每个判定条件写两条用例,条件组合随意凑一个;只测试了控制流,没有测试数据流,状态变量的定义、覆盖和残留情况不做检查等情况,很多人认为单元测试没有达到应有的作用。
希望通过这篇文章,能够对看过的软件开发和测试人员有所启发,理解软件测试测试的目的不是为了跑完代码,而是为了证明代码逻辑和数据行为是可控的。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:薄说安全 薄说安全《一文看懂软件控制流用例设计方法》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论