4. 动态测试工具链:Tessy / VectorCAST 的工程创建、桩函数与驱动函数自动生成

动态测试工具链,说白了就是让代码真正跑起来,然后看它是不是按我们预期工作的。Tessy 和 VectorCAST 是嵌入式领域用得最多的两个工具。我这些年两个工具都用过,说实话,各有各的脾气,但核心思路是相通的。

这一章,我们就来聊聊怎么创建工程,以及怎么让工具自动生成桩函数(Stub)和驱动函数(Driver)。这是动态测试的基石,搞不定这两步,后面的测试用例设计就是空中楼阁。

4.1 工程创建:从零到一的关键几步

不管是 Tessy 还是 VectorCAST,创建工程的第一步都是告诉工具:你要测什么代码,跑在什么平台上。

4.1.1 Tessy 工程创建流程

Tessy 的工程创建,我个人习惯按这个步骤来:

  1. 新建工程:选择 File → New → Project,指定工程名称和存储路径。
  2. 导入源文件:把被测的 .c 和 .h 文件加进来。Tessy 会自动解析函数接口。
  3. 配置编译器:选择目标平台的交叉编译器。这一步容易出错,我遇到过好几次因为编译器路径配错导致编译失败的情况。
  4. 设置测试级别:单元测试选 TUnit,集成测试选 TIntegration。
  5. 保存工程:Tessy 会生成 .tbp 文件,这就是你的工程文件。
小技巧:导入源文件时,建议先把头文件路径配好。Tessy 解析依赖时,头文件找不到会报一堆莫名其妙的错误。

4.1.2 VectorCAST 工程创建流程

VectorCAST 的创建方式稍微不同,它更强调“环境”的概念:

  1. 新建环境:打开 VectorCAST/Cover,选择 File → New Environment。
  2. 选择编译器:从列表里选你的目标编译器,或者自定义。
  3. 添加源文件:把被测文件拖进去。VectorCAST 会自动分析函数调用关系。
  4. 设置测试单元:选择你要测的函数,工具会生成对应的测试框架。
  5. 编译环境:点击 Build,工具会编译整个测试环境。
注意:VectorCAST 对环境路径中的中文和空格非常敏感。我曾经因为项目路径里带了个中文“测试”两个字,折腾了半天才发现是路径问题。

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;
}
核心区别:Tessy 的桩函数更“轻量”,你需要手动配置返回值;VectorCAST 的桩函数更“重”,自带调用记录和参数检查功能。

4.3 驱动函数(Driver)自动生成:让测试跑起来

驱动函数是测试的“总指挥”。它负责:

  • 初始化被测函数需要的全局变量和硬件状态。
  • 调用被测函数,传入测试参数。
  • 收集被测函数的输出结果,和预期值做比较。

手动写驱动函数?一个函数几十行,几十个函数就是上千行。工具自动生成,省时省力。

4.3.1 Tessy 的驱动函数生成

Tessy 的驱动函数生成是“半自动”的:

  1. 在 TDE 中,Tessy 会列出被测函数的所有参数和返回值。
  2. 你只需要为每个参数填写测试值,Tessy 会自动生成驱动代码。
  3. 驱动代码会包含:参数赋值、函数调用、结果比较。

生成的驱动代码示例:

// 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);
}
我的经验:VectorCAST 的驱动生成虽然自动化程度高,但生成的代码可读性不如 Tessy。如果你需要维护大量测试用例,Tessy 的驱动代码更容易理解和修改。

4.4 核心流程对比:一张图看懂

为了让你更直观地理解两个工具的流程差异,我画了张对比图:

Tessy vs VectorCAST 工程创建与代码生成流程对比 Tessy 流程 1. 新建工程 (.tbp) 2. 导入源文件 + 头文件路径 3. 配置编译器 + 测试级别 4. 手动创建 Stub (右键) 5. 填写测试数据 → 自动生成 Driver VectorCAST 流程 1. 新建 Environment 2. 选择编译器 + 添加源文件 3. 选择测试单元 → Build 环境 4. Stub 自动生成 (无需手动) 5. Data Panel 填值 → 自动生成 Driver 核心差异 Tessy: 半自动 VectorCAST: 全自动

4.5 避坑指南:我踩过的那些坑

做动态测试工具链配置,有几个坑我印象特别深:

  • 头文件依赖:Tessy 对头文件的解析比较严格。我曾经因为一个头文件里用了 #ifdef 条件编译,导致 Tessy 解析出来的函数接口和实际不符。解决办法是手动指定预定义宏。
  • 全局变量初始化:VectorCAST 自动生成的驱动函数不会初始化全局变量。如果你的被测函数依赖全局变量的初始值,记得在测试用例里手动设置。我吃过这个亏,测试结果一直不对,查了半天才发现是全局变量没初始化。
  • 桩函数的返回值类型:Tessy 的桩函数默认返回 0,但如果函数返回的是指针,0 就是 NULL。如果你的代码没有做 NULL 检查,直接解引用就会崩溃。嗯,这个坑我至少踩过三次。
重要提醒:不管是 Tessy 还是 VectorCAST,自动生成的桩函数和驱动函数都只是“骨架”。你需要根据实际的测试需求,手动调整桩函数的返回逻辑和驱动函数的断言条件。工具帮你省了 80% 的体力活,但剩下的 20% 脑力活,还得自己来。

4.6 小结

这一章我们聊了 Tessy 和 VectorCAST 的工程创建、桩函数和驱动函数的自动生成。说白了,工具的核心价值就是帮你把重复性的工作自动化,让你能把精力放在测试用例的设计上。

我个人更倾向于:如果项目规模小、函数接口简单,用 Tessy 更灵活;如果项目规模大、需要大量回归测试,VectorCAST 的自动化程度更高。当然,这只是我的个人习惯,你可以根据实际情况选择。