4. 自动化测试框架选型:主流框架对比与决策

做嵌入式测试这么多年,我见过太多团队在框架选型上栽跟头。有的选了重型框架,结果测试环境搭了两周还没跑通第一个用例。有的选了轻量级方案,后期维护却成了噩梦。说白了,没有最好的框架,只有最适合你项目的框架。

这一章,我就把 Robot Framework、pytest 和 Unity/CMock 这三套主流方案掰开揉碎讲清楚。最后再给一个决策矩阵,帮你快速锁定方向。

4.1 三大主流框架速览

先看一张对比表,心里有个底:

维度 Robot Framework pytest Unity / CMock
语言 关键字驱动(Python/Java 扩展) 纯 Python C 语言
适用层级 系统测试、验收测试 单元测试、集成测试、系统测试 单元测试、模块测试
学习曲线 中等(关键字语法需适应) 低(Python 基础即可) 低(C 语言原生)
硬件依赖 弱(可纯模拟) 中(需插件支持) 强(直接跑在目标板)
报告能力 强(自带 HTML/XML 报告) 中(需插件增强) 弱(需自建报告)
Mock 支持 需第三方库 内置 monkeypatch / pytest-mock CMock 原生支持

4.2 Robot Framework:关键字驱动的系统测试利器

Robot Framework 最大的特点是「可读性」。测试用例写出来像自然语言,非技术人员也能看懂。我在一个车载娱乐系统项目里用过它,测试用例直接给产品经理评审,效率很高。

举个例子,一个简单的测试用例:

*** Settings ***
Library    SerialLibrary

*** Test Cases ***
验证设备启动日志
    [Setup]    打开串口    /dev/ttyUSB0    115200
    发送命令    reboot
    等待日志    5s
    日志应包含    "Kernel init done"
    [Teardown]  关闭串口

你看,每一步都清晰明了。但要注意,Robot Framework 的底层执行效率不高。如果测试用例超过 500 个,执行时间会明显变长。我建议把它用在「冒烟测试」和「回归测试」的场景,而不是跑全量用例。

我的习惯:Robot Framework 适合做「黑盒验证」。比如设备上电后,通过串口或网络发送指令,检查返回结果。它不适合做底层的内存泄漏检测或时序分析。

4.3 pytest:Python 生态的瑞士军刀

pytest 是我个人最常用的框架。为什么?因为它灵活。从单元测试到系统测试,从纯软件模拟到硬件交互,它都能搞定。

看一个嵌入式场景下的 pytest 用例:

import serial
import pytest

@pytest.fixture
def serial_port():
    port = serial.Serial('/dev/ttyUSB0', 115200, timeout=1)
    yield port
    port.close()

def test_boot_log(serial_port):
    serial_port.write(b'reboot\n')
    response = serial_port.read_until(b'Kernel init done', timeout=5)
    assert b'Kernel init done' in response

pytest 的 fixture 机制非常强大。你可以用它管理硬件资源、模拟外部依赖、做参数化测试。我曾经在一个物联网网关项目里,用 pytest 配合 pytest-xdist 做并行测试,把 2000 个用例的执行时间从 40 分钟压缩到 8 分钟。

避坑指南:pytest 的插件生态虽然丰富,但有些插件在嵌入式场景下不兼容。比如 pytest-asyncio 在单线程硬件上可能出问题。我建议先在小范围验证,再全量推广。

4.4 Unity / CMock:C 语言测试的黄金搭档

如果你的测试对象是裸机程序或 RTOS 上的 C 代码,Unity + CMock 几乎是标配。Unity 负责执行测试用例,CMock 负责生成 Mock 对象。

看一个 Unity 测试用例:

#include "unity.h"
#include "temperature_sensor.h"

void setUp(void) {}
void tearDown(void) {}

void test_temperature_reading_should_be_in_range(void) {
    int16_t temp = read_temperature();
    TEST_ASSERT_INT16_WITHIN(5, 25, temp);
}

int main(void) {
    UNITY_BEGIN();
    RUN_TEST(test_temperature_reading_should_be_in_range);
    return UNITY_END();
}

CMock 的用法也很直接。假设你有一个 I2C 驱动,你想测试上层逻辑,但不希望真的操作硬件:

// 自动生成 Mock
#include "Mocki2c_driver.h"

void test_sensor_init_should_configure_i2c(void) {
    i2c_init_Expect(0x48, 100000);
    sensor_init();
}

Unity 的执行效率极高,因为它就是纯 C 代码,没有解释器开销。我在一个 STM32 项目里,直接在目标板上跑 Unity 测试,500 个用例 3 秒跑完。

核心优势:Unity/CMock 是唯一能「在目标硬件上直接运行」的框架。这意味着你可以测试中断响应、DMA 传输、外设时序等底层行为。这是 pytest 和 Robot Framework 做不到的。

4.5 框架选型决策矩阵

选型不能拍脑袋。我整理了一个决策矩阵,从 6 个维度打分(1-5 分),帮你量化判断:

评估维度 Robot Framework pytest Unity/CMock
硬件交互能力 3 3 5
用例可读性 5 4 3
执行效率 2 4 5
扩展性 4 5 2
社区生态 4 5 3
学习成本 3 5 4

怎么用这个矩阵?

  • 总分 20-25:框架与项目高度匹配,可以直接采用。
  • 总分 15-19:框架可用,但需要做一些定制或补充。
  • 总分 10-14:框架与项目匹配度低,建议重新考虑。

举个例子,一个典型的「嵌入式单元测试」场景:

  • 硬件交互能力:5(必须跑在目标板)
  • 执行效率:5(用例多,要求快速反馈)
  • 可读性:3(开发人员看就行)

算下来 Unity/CMock 总分 13,pytest 只有 11。所以选 Unity/CMock 更合理。

4.6 轻量级框架定制:从零搭一个够用的测试框架

有时候,现成的框架太重了。比如你的 MCU 只有 64KB Flash,跑 Unity 都嫌大。这时候就需要自己定制一个轻量级框架。

我分享一个我在 MSP430 项目里用过的方案,核心代码不到 50 行:

#include <stdio.h>

#define TEST_ASSERT(cond, msg) \
    do { \
        if (!(cond)) { \
            printf("FAIL: %s at %s:%d\n", msg, __FILE__, __LINE__); \
            failures++; \
        } else { \
            printf("PASS: %s\n", msg); \
            passes++; \
        } \
    } while(0)

static int passes = 0;
static int failures = 0;

void test_adc_reading(void) {
    int reading = read_adc(0);
    TEST_ASSERT(reading > 0, "ADC reading should be positive");
    TEST_ASSERT(reading < 4096, "ADC reading should be within 12-bit range");
}

int main(void) {
    test_adc_reading();
    printf("\n%d passed, %d failed\n", passes, failures);
    return failures;
}

这个框架虽然简陋,但够用。它只有三个要素:

  • 断言宏:检查条件,记录结果
  • 计数器:统计通过/失败数量
  • 报告输出:通过串口或日志输出结果

如果你需要更复杂的功能,可以逐步添加:

  • 测试分组(按模块分类)
  • 超时检测(防止死循环)
  • 内存统计(检测堆栈溢出)
我的建议:轻量级框架适合「资源受限」或「快速原型」的场景。但一旦项目超过 100 个用例,还是建议切换到 Unity 这样的成熟框架。别问我怎么知道的——我曾经在一个项目里手写了 300 个自定义断言,后期维护到崩溃。

4.7 选型决策流程图

下面这张 SVG 流程图,帮你把选型思路串起来:

自动化测试框架选型决策流程 自动化测试框架选型决策流程 开始选型 测试层级是什么? 单元测试 / 集成测试 / 系统测试 单元测试 系统测试 集成测试 Unity / CMock C语言原生,跑在目标板 pytest 灵活,支持Mock和参数化 Robot Framework 关键字驱动,可读性强 资源是否受限? Flash < 64KB / RAM < 8KB 是 否 轻量级定制框架 自定义断言 + 串口输出 使用成熟框架 按决策1选择对应框架 提示:实际项目中常混合使用多个框架。例如:单元测试用 Unity,系统测试用 Robot Framework。

这张图的核心逻辑很简单:先看测试层级,再看资源限制。单元测试优先考虑 Unity/CMock,系统测试优先考虑 Robot Framework,集成测试则 pytest 更灵活。如果资源受限,不管哪个层级,都建议走轻量级定制路线。

最后说一句:框架选型不是一锤子买卖。项目初期可能只需要一个轻量级方案,随着测试规模扩大,再逐步迁移到更成熟的框架。保持开放心态,别被「框架信仰」束缚住。