4. 动态测试工具链:Tessy / VectorCAST 的工程创建、桩函数与驱动函数自动生成
动态测试工具链,说白了就是让代码真正跑起来,然后看它是不是按我们预期工作的。Tessy 和 VectorCAST 是嵌入式领域用得最多的两个工具。我这些年两个工具都用过,说实话,各有各的脾气,但核心思路是相通的。
这一章,我们就来聊聊怎么创建工程,以及怎么让工具自动生成桩函数(Stub)和驱动函数(Driver)。这是动态测试的基石,搞不定这两步,后面的测试用例设计就是空中楼阁。
4.1 工程创建:从零到一的关键几步
不管是 Tessy 还是 VectorCAST,创建工程的第一步都是告诉工具:你要测什么代码,跑在什么平台上。
4.1.1 Tessy 工程创建流程
Tessy 的工程创建,我个人习惯按这个步骤来:
- 新建工程:选择 File → New → Project,指定工程名称和存储路径。
- 导入源文件:把被测的 .c 和 .h 文件加进来。Tessy 会自动解析函数接口。
- 配置编译器:选择目标平台的交叉编译器。这一步容易出错,我遇到过好几次因为编译器路径配错导致编译失败的情况。
- 设置测试级别:单元测试选 TUnit,集成测试选 TIntegration。
- 保存工程:Tessy 会生成 .tbp 文件,这就是你的工程文件。
4.1.2 VectorCAST 工程创建流程
VectorCAST 的创建方式稍微不同,它更强调“环境”的概念:
- 新建环境:打开 VectorCAST/Cover,选择 File → New Environment。
- 选择编译器:从列表里选你的目标编译器,或者自定义。
- 添加源文件:把被测文件拖进去。VectorCAST 会自动分析函数调用关系。
- 设置测试单元:选择你要测的函数,工具会生成对应的测试框架。
- 编译环境:点击 Build,工具会编译整个测试环境。
4.2 桩函数(Stub)自动生成:让被测函数独立运行
桩函数是什么?说白了,就是替身。当被测函数调用了外部函数(比如硬件驱动、OS 接口),我们需要用桩函数来模拟这些外部行为。
手动写桩函数?太累了。工具可以帮我们自动生成。
4.2.1 Tessy 的桩函数生成
Tessy 的桩函数生成非常直观:
- 在 TDE(Test Data Editor)中,Tessy 会自动识别所有外部函数调用。
- 你只需要右键点击外部函数,选择“Create Stub”。
- Tessy 会生成一个默认的桩函数,返回类型对应的默认值(比如 int 返回 0,指针返回 NULL)。
生成的桩函数代码大概长这样:
// Tessy 自动生成的桩函数示例
stub_ret_t Stub_ReadSensor(uint8_t sensorId, uint16_t* value)
{
/* 默认实现:返回成功,值设为 0 */
*value = 0;
return STUB_OK;
}
嗯,这里要注意:默认桩函数只是占位,你需要根据测试用例来修改返回值。Tessy 允许你在 TDE 中直接设置每个测试用例的桩函数返回值,非常方便。
4.2.2 VectorCAST 的桩函数生成
VectorCAST 的桩函数生成更自动化一些:
- 创建环境后,VectorCAST 会自动为所有外部函数生成桩函数。
- 在 CAST 窗口中,你可以看到所有桩函数的列表。
- 每个桩函数都有默认行为:返回 0 或 NULL,并且记录调用次数和参数。
VectorCAST 的桩函数代码:
// VectorCAST 自动生成的桩函数示例
int Stub_ReadSensor(uint8_t sensorId, uint16_t* value)
{
/* 默认行为:返回 0,记录调用 */
CAST_Stub_Return_Int("ReadSensor", 0);
return 0;
}
4.3 驱动函数(Driver)自动生成:让测试跑起来
驱动函数是测试的“总指挥”。它负责:
- 初始化被测函数需要的全局变量和硬件状态。
- 调用被测函数,传入测试参数。
- 收集被测函数的输出结果,和预期值做比较。
手动写驱动函数?一个函数几十行,几十个函数就是上千行。工具自动生成,省时省力。
4.3.1 Tessy 的驱动函数生成
Tessy 的驱动函数生成是“半自动”的:
- 在 TDE 中,Tessy 会列出被测函数的所有参数和返回值。
- 你只需要为每个参数填写测试值,Tessy 会自动生成驱动代码。
- 驱动代码会包含:参数赋值、函数调用、结果比较。
生成的驱动代码示例:
// Tessy 自动生成的驱动函数片段
void TCase_CalculateSpeed(void)
{
/* 设置输入参数 */
uint16_t rpm = 3000;
uint8_t gear = 2;
/* 调用被测函数 */
uint16_t speed = CalculateSpeed(rpm, gear);
/* 比较结果 */
TEST_CHECK("speed == 60", speed == 60);
}
4.3.2 VectorCAST 的驱动函数生成
VectorCAST 的驱动生成更“全自动”:
- 创建测试用例时,VectorCAST 会自动生成驱动框架。
- 你只需要在 Data Panel 中填写输入值和预期输出值。
- 工具会自动生成完整的驱动函数,包括参数传递、函数调用、结果断言。
VectorCAST 的驱动代码:
// VectorCAST 自动生成的驱动函数片段
void Test_CalculateSpeed_001(void)
{
/* 设置输入 */
CAST_Set_Arg("CalculateSpeed", 0, 3000); // rpm
CAST_Set_Arg("CalculateSpeed", 1, 2); // gear
/* 调用被测函数 */
CAST_Call("CalculateSpeed");
/* 检查返回值 */
CAST_Check_Return("CalculateSpeed", 60);
}
4.4 核心流程对比:一张图看懂
为了让你更直观地理解两个工具的流程差异,我画了张对比图:
4.5 避坑指南:我踩过的那些坑
做动态测试工具链配置,有几个坑我印象特别深:
- 头文件依赖:Tessy 对头文件的解析比较严格。我曾经因为一个头文件里用了 #ifdef 条件编译,导致 Tessy 解析出来的函数接口和实际不符。解决办法是手动指定预定义宏。
- 全局变量初始化:VectorCAST 自动生成的驱动函数不会初始化全局变量。如果你的被测函数依赖全局变量的初始值,记得在测试用例里手动设置。我吃过这个亏,测试结果一直不对,查了半天才发现是全局变量没初始化。
- 桩函数的返回值类型:Tessy 的桩函数默认返回 0,但如果函数返回的是指针,0 就是 NULL。如果你的代码没有做 NULL 检查,直接解引用就会崩溃。嗯,这个坑我至少踩过三次。
4.6 小结
这一章我们聊了 Tessy 和 VectorCAST 的工程创建、桩函数和驱动函数的自动生成。说白了,工具的核心价值就是帮你把重复性的工作自动化,让你能把精力放在测试用例的设计上。
我个人更倾向于:如果项目规模小、函数接口简单,用 Tessy 更灵活;如果项目规模大、需要大量回归测试,VectorCAST 的自动化程度更高。当然,这只是我的个人习惯,你可以根据实际情况选择。