在当今复杂多变的软件开发环境中,软件系统的规模和复杂度不断攀升,传统测试方法面临着诸多挑战。如何高效、准确地生成测试用例,以确保软件系统的质量和可靠性,成为软件测试领域的关键问题之一。而基于模型的测试用例生成(Model-Based Test Case Generation)作为一种新兴且高效的测试方法,正逐渐成为解决这一问题的重要手段。
01
引 言
在传统的软件测试过程中,测试用例多由人工基于源代码撰写,往往依赖于开发人员或测试工程师对需求与代码的经验和直觉。这种方法虽然在一定程度上能够发现一些明显的缺陷,但随着软件系统的复杂度增加,其局限性愈发明显。开发人员很难全面覆盖所有的测试场景,尤其是那些隐藏在复杂逻辑和交互中的潜在问题。此外,传统测试用例生成方法的效率较低,难以满足快速迭代和频繁更新的软件开发需求。
基于模型的测试用例生成(Model-Based Test Case Generation)应运而生,它通过构建软件系统的模型,将测试用例的生成从繁琐的人工设计转变为基于模型的自动化过程。这种方法不仅能够提高测试用例的质量和覆盖率,还能显著提升测试效率,减少测试成本,将开发人员与测试人员从复杂的需求与代码中解放出来。
在基于模型的测试中,软件系统的设计模型是核心。它以具体的模型描述系统的结构、行为和约束条件,通过对设计模型的构建、分析和遍历,利用符号执行方法与SMT求解器(Satisfiability Modulo Theories Solver)自动生成测试用例,覆盖系统的关键功能和潜在故障点。这种方法的优势在于,它能够从系统的整体视角出发,考虑各种可能的交互和状态转换,从而生成更为全面和有效的测试用例。
在接下来的内容中,我们将深入探讨基于模型的测试用例生成,并结合Lustre这一独特的同步数据流语言,展示符号执行在基于模型的测试用例执行中的应用和优势。通过实际案例和分析,揭示基于模型的测试用例生成方法如何帮助开发人员和测试工程师更高效地发现软件缺陷,提升软件质量。
02
核心技术
基于模型的测试用例生成主要包含两个核心技术,分别是设计模型构建和解析与符号执行生成用例。
(一)设计模型构建和解析
在基于模型的测试中,设计模型是测试用例生成的基础。设计模型的构建通常需要一个直观且易于操作的界面,以便开发人员和测试人员能够根据需求快速搭建出系统的模型。而对于Lustre语言,设计模型的构建和解析尤为重要,因为Lustre的同步数据流特性需要精确地描述系统的时序行为。其主要流程如下:
➤ 通过可视化界面搭建设计模型:设计模型的构建通常基于图形化组件,这些组件可以是节点、边、状态机状态等。每个组件都有明确的语义,例如节点表示一个功能模块,边表示数据流或控制流。可视化界面支持拖放操作,用户可以通过拖动组件并将其放置在设计区域来构建模型。这种直观的操作方式大大降低了模型构建的难度。支持层次化结构的构建,用户可以将复杂的系统分解为多个子系统,并在不同的层次上进行建模。这种层次化结构不仅有助于理解系统的整体结构,还能提高模型的可维护性。
➤ 可视化模型到Lustre语言的转化:设计模型中的每个节点需要映射为Lustre语言中的一个节点(Node)。节点的输入输出信号需要与设计模型中的接口一致。设计模型中的数据流边需要转化为Lustre语言中的数据连接。每个边的源节点输入需要连接到目标构件的输出。设计模型中的控制流逻辑(如分支和循环)需要转化为Lustre语言中的条件语句和迭代器结构。
➤ 中间模型的生成:使用Lustre编译器或解析器工具,将Lustre程序解析为抽象语法树(AST)。解析过程需要确保语法和语义的正确性。从AST中提取数据流图,包括节点、边和信号类型。数据流图的每个节点对应Lustre程序中的一个节点,每条边对应一个等式连接,所有的节点与等式都转化为可供操作的C++语言对象或实例。完成中间模型的转化后还要对其进行静态检查,以保证模型的正确性与一致性。
(二)符号执行生成用例
符号执行是一种动态分析技术,它通过分析输入的中间模型(C++语言实例),符号化输入和路径条件,探索程序的所有可能执行路径。符号执行的核心思想是将程序的输入表示为符号变量,而不是具体的数值。这些符号变量在程序的执行过程中被逐步约束,最终通过求解约束条件生成具体的测试用例。
➤ 符号变量与路径条件:在符号执行中,程序的输入被表示为符号变量。 这些符号变量可以是整数、浮点数或布尔值等。符号变量在程序的执行过程中被逐步约束。路径条件是符号执行中用于描述程序执行路径的逻辑表达式。每个路径条件表示从程序入口到当前节点的路径上所有分支条件的合取。路径条件的求解结果决定了程序的执行路径。
➤ 约束求解:在符号执行过程中,程序的每一条路径都被表示为一组约束条件。这些约束条件是路径条件的集合,描述了程序在该路径上的输入输出关系。符号执行需要一个强大的约束求解器来求解生成的约束条件。常见的约束求解器包括Z3、CVC5等。这些求解器能够处理线性方程、非线性方程、布尔逻辑等多种约束条件。
➤ 测试用例生成:符号执行通过逐步探索程序的所有可能路径,生成路径条件。对于每个路径条件,约束求解器尝试找到满足条件的输入值。如果约束求解器能够找到满足路径条件的输入值,则这些输入值被用作测试用例。符号执行的目标是生成覆盖程序所有路径的测试用例。
03
实例解析
本章节通过真实世界中的飞机机翼转向控制系统中的一个警告子模块实例对基于模型的测试用例生成进行详细说明。该警告子模块的需求为,输入左右机翼的转向率,与预先设定的常数分别进行比较,并将结果返回。
(一)设计模型构建
根据需求进行设计模型的构建,其可视化模型如下所示。
图1 机翼转向控制系统警告子模块可视化模型
该模型包含一个输入与两个输出。输入为rollRate,表示当前飞机的预期的转向率,输出分别为左机翼与右机翼的警告情况。在可视化模型的最左侧为常量kRollRateWarning,其类型为结构体,结构体包含两个浮点数。常量kRollRateWarning经过Flatten构件(拆分结构体构件)裂化为两个浮点数分别与rollRate进行大小比较,将比较结果作为输出返回。
将该设计模型转换为Lustre语言代码,并确保其能通过检查。其Lustre语言代码如下所示。
图2 机翼转向控制系统警告子模块Lustre模型
最后进行中间模型转换,以将Lustre模型转换为可直接操纵的C++语言模型。通过对输入的Lustre语言模型进行分析,生成相应的抽象语法树。继而通过对生成的抽象数据字典的管理,来实现中间模型中定义的数据变量关系的转换与存储。最终,作为输入的Lustre语言模型将转化为具有结构化与层次化信息,且便于操作的C++语言中间模型。
图3 中间模型转换流程图
(二)符号执行生成用例
对给定的 Lustre 函数 RollRateWarning 使用符号执行方法,包括符号执行过程、约束条件生成与求解、测试用例生成等关键步骤。
➤ 符号化输入:将输入 rollRate 符号化为符号变量 rollRate_sym。
➤ 路径条件生成:根据函数逻辑,我们可以得到以下路径条件:
leftWarning 的路径条件:rollRate_sym < _L15
rightWarning 的路径条件:rollRate_sym > _L16
由于_L15与_L16是由常数kRollRateWarning裂化而来,在当前场景下kRollRateWarning被设定为(-15.0,15.0)。因此路径条件实际上分别为:
rollRate_sym < -15.0与rollRate_sym >15
➤ 约束求解:需要求解以下约束条件,以生成符合航空航天软件中广泛使用的MC/DC覆盖率准则下的测试用例。
对于 leftWarning 为 true 的情况:rollRate_sym < -15.0
对于 leftWarning 为 false 的情况:rollRate_sym >= -15.0
对于 rightWarning 为 true 的情况:rollRate_sym > 15.0
对于 rightWarning 为 false 的情况:rollRate_sym <= 15.0
➤ 测试用例生成:通过对上述约束组合后作为约束求解器Z3的输入以进行求解,能够得到以下最简测试用例集。
测试用例 1:rollRate = -16.0(满足 leftWarning = true 和 rightWarning = false)
测试用例 2:rollRate = -15.0(满足 leftWarning = false 和 rightWarning = false)
测试用例 3:rollRate =16(满足 leftWarning = false 和 rightWarning = true)
通过符号执行方法,我们可以对 Lustre 函数 RollRateWarning 进行全面的测试。符号执行不仅能够生成覆盖所有路径的测试用例,还能发现潜在的边界条件和异常情况。结合 Lustre 语言的特性,符号执行为实时嵌入式系统的测试提供了一种高效、可靠的方法。
04
优势总结
在软件开发领域,测试用例生成是确保软件质量的关键环节。基于模型的测试用例生成作为一种先进的测试方法,相较于传统测试用例生成方法,具有多方面的显著优势。
(一) 提高测试覆盖率:通过构建软件系统的模型,能够全面覆盖系统的各种行为和状态转换。模型可以精确地描述系统的功能、业务流程以及输入输出之间的关系,确保测试用例能够覆盖所有关键路径和边界条件。
(二) 提升测试效率:能够自动化地从模型中生成测试用例,大大减少了人工编写测试用例的时间和精力。测试人员只需专注于模型的构建和维护,而无需花费大量时间在测试用例的编写上。当软件需求发生变更时,只需更新模型,方法就可以快速重新生成测试用例,以适应新的需求。这种快速响应能力使得该方法在敏捷开发和持续集成环境中具有显著优势。
(三) 增强测试可维护性:测试用例是基于模型生成的,因此测试用例的维护可以转化为对模型的维护。模型的修改和维护相对简单,且更容易保持一致性。当模型更新时,所有相关的测试用例可以同步更新,避免了传统测试中测试用例分散、难以维护的问题。
(四) 提升测试质量:由于测试用例是基于模型自动生成的,因此可以减少因人工编写测试用例而引入的错误。自动生成的测试用例更加客观、准确,能够更有效地发现软件缺陷。生成的测试用例具有高度的一致性和可重复性。在相同的模型和生成规则下,每次生成的测试用例都是相同的,这有助于测试结果的比较和分析。
05
技术应用
随着科技发展与技术创新,近年来轨交、汽车、航空航天等领域的发展非常迅猛,伴随而来的是各类安全问题。以上领域涉及的安全关键软件的质量与效率都受到了高度重视。因此,可视化建模工具SmartRocket Modeler应运而生,诞生自质量,扎根于安全。通过模型语言的图形化建模、模型静态检查、仿真与调试以及C代码生成等等功能为用户提供一套基于模型的高安全性嵌入式软件解决方案。在更进一步的前提下逐渐替代相关国外软件,为解决卡脖子难题做出贡献。
图4 SmartRocket Modeler项目管理界面图
依托模型驱动测试用例生成技术,上海控安 SmartRocket Modeler 团队通过深入调研与持续实践,实现了从理论到应用的有效转化。
Modeler的输入为需求模型,通过工程师的建模或导入生成方法得到其设计模型,其设计模型能够通过图形化或者代码字符串的方式进行展示,如下图所示为Modeler官方示例项目Roll Control的图形化界面。
图5 SmartRocket Modeler设计模型图形化展示界面
Modeler使用同步数据流语言Lustre代码进行设计模型的一致性描述,Lustre是一种经过严格形式化验证的设计模型语言,它能够充分描述机载软件在固定时钟下的运行情况,其示例代码如下所示。
图6 RollControl飞机控制RollRateWarning模块Lustre代码示意图
Modeler通过本文的完整方法与理论,针对设计模型自动生成满足MC/DC覆盖率的准则的测试用例。点击图形界面中的“生成用例”按钮,工具会根据当前模型节点信息生成用例。
图7 图形界面“生成用例”按钮
用例生成完毕后,工具会将所有用例集成到Excel表格中,并提供管理与查看功能。
图8 图形界面用例下载管理器
针对该模型节点,即机翼转向控制系统警告子模块,得出的全部测试用例如下。
图9 测试用例生成结果
已完成
数据加载中