Files
STM32F4-Base/docs/测试开发标准流程.md
2026-08-22 19:53:24 +08:00

6.2 KiB
Raw Blame History

嵌入式测试开发标准流程(基于 STM32F4 + CH395F 经验)

适用范围STM32 裸机/FreeRTOS 项目Keil MDK-ARM v5 (ARMCC),串口日志输出测试结果。


1. 测试规划(文档先行)

docs/ 下创建测试规范文档,定义以下结构:

1.1 阶段概览表

阶段 ID 类型 耗时 入口 出口
TM-PHY-01 独立运行 ~1s 宏启用 串口汇总

1.2 每个测试用例

字段 说明
ID TC-NET-NNN,唯一编号
优先级 P0核心/ P1重要
类型 功能测试 / 边界测试 / 负向测试 / 压力测试 / 恢复测试
标题 一句话描述
前置条件 硬件状态、PC 端命令、依赖的其他用例
测试步骤 编号操作序列
成功标准 精确到"返回值 == 0x00"、"延迟 >= 60000ms"
失败标准 每种失败对应的现象
覆盖原则 对应哪个设计原则

关键原则:先写判定标准,再写测试代码,避免"测了但不知道算不算过"。


2. 测试框架搭建

2.1 目录结构

test/
  ch395f_test.h           # 宏定义 + TEST_CHECK/TEST_REPORT + test_stats_t
  ch395f_test_task.h      # 仅导出 StartCh395fTestTask
  ch395f_test_task.c      # 全 Phase 实现

2.2 模板文件

ch395f_test.h — 宏+类型

// 阶段使能开关(取消注释即启用)
#define ENABLE_PHASE1_TESTS

// 测试统计
typedef struct {
    uint16_t total;
    uint16_t passed;
    uint16_t failed;
} test_stats_t;

extern test_stats_t g_test_stats;

#define TEST_CHECK(cond, fmt, ...) do { \
    g_test_stats.total++; \
    if (cond) { \
        g_test_stats.passed++; \
        DBG_INFO("[PASS] " fmt, ##__VA_ARGS__); \
    } else { \
        g_test_stats.failed++; \
        DBG_ERROR("[FAIL] " fmt, ##__VA_ARGS__); \
    } \
} while (0)

#define TEST_REPORT(name) do { \
    DBG_INFO("=== %s: %d/%d PASSED (failed=%d) ===", \
             name, g_test_stats.passed, g_test_stats.total, g_test_stats.failed); \
} while (0)

ch395f_test_task.c — 每个 Phase 的模板

#ifdef ENABLE_PHASEX_TESTS
/*
 * Phase X — 功能说明
 *
 * 测试目的:(概括)
 * 前置条件:(硬件/软件/PC 端)
 * 通过:(整体判定条件)
 * 失败:(整体判定条件)
 */
static void phaseX_run(void) {
    DBG_INFO("=== CH395F Phase X Tests ===");

    /* ---- TC-NET-NNN: 用例标题 ---- */
    {
        // 原理说明
        // 通过:精确条件
        // 失败:精确条件
        ret = some_api();
        DBG_INFO("          expect: ...");
        DBG_INFO("          actual: ...");
        TEST_CHECK(ret == EXPECTED, "description");
    }

    TEST_REPORT("Phase X");
}
#endif

3. 编码规范

3.1 DBG_INFO 三行输出

每个判定点输出三行,一眼看出"期望什么、拿了什么、过没过"

  [P1-01] ch395f_check_exist()
          expect: 0x00 (~0x57 = 0xA8)
          actual: 0x00
  [PASS] P1-01 check_exist = 0x00 (expect 0x00)

3.2 硬编码值加注释

// 好:
ch395f_status_t ret = ch395f_check_exist();
// SPI 写入 0x06 (CMD_CHECK_EXIST) + 0x57 (测试字节)
// 成功:回复 == ~0x57 == 0xA8CH395F_ERR_SUCCESS = 0x00
// 失败:回复 != 0xA8CH395F_STATUS_NOT_DETECTED = 0xFF

// 不好:
ch395f_status_t ret = ch395f_check_exist();
TEST_CHECK(ret == 0, "check_exist OK");

3.3 无中文字符串

ARMCC v5 不识别 UTF-8 多字节字符。DBG_INFO/DBG_ERROR 中只写 ASCII。注释可以写中文。

3.4 栈安全

大缓冲区(如 64KBstatic 全局,不放任务栈:

#define TEST_BUF_SIZE       65536
static uint8_t s_rx_buf[TEST_BUF_SIZE];

3.5 条件编译消除未使用变量警告

static uint8_t s_rx_buf[TEST_BUF_SIZE];
#if defined(ENABLE_PHASE5_TESTS) || defined(ENABLE_PHASE8_TESTS)
static uint8_t s_tx_buf[TEST_BUF_SIZE];
#endif

4. 统一入口

4.1 所有测试在 FreeRTOS 任务中串行执行

ch395fTestTask:
  1. 等待 g_net_readynetTask 初始化完成)
  2. 阻塞执行 Phase 1/2/3/6/9内部循环无需外部连接
  3. 创建 TCP 监听 Socket端口 8080单连接模式
  4. 事件驱动循环 Phase 4/5/7/8/10等待 PC 连接)

4.2 不要在 main.c 中直接调用测试

main.c 只做硬件初始化和启动 OS不包含任何测试逻辑。

4.3 测试文件的手动注册

新测试文件需:

  1. 添加到 test/ 目录
  2. 修改 MDK-ARM/STM32F407-Demo.uvprojx 添加文件引用和 IncludePath

5. 文档与代码同步

  • 文档中的入口函数名、宏名、流程描述必须与代码一致
  • 代码变更后立即更新文档,否则文档两天内就会失效
  • 测试用例的通过/失败标准在文档和代码注释中都写清楚
  • 文档使用标准 Markdown 表格,避免复杂嵌套导致渲染异常

6. 编译验证

6.1 底线

0 Error(s), 0 Warning(s),不可妥协。

6.2 常见警告处理

警告 原因 解决
#177-D: variable was declared but never referenced 条件编译导致 #ifdef 包裹变量声明
#870-D: invalid multibyte character sequence DBG_INFO 中有中文 改为纯 ASCII
隐式类型转换 参数类型不匹配 加显式 (uint8_t) 等 cast

6.3 构建命令

MDK-ARM\build.bat
:: 或项目根目录执行 @build

退出码0 = 成功 | 1 = 有警告(不通过)| 2+ = 错误


7. 迭代节奏

文档(规划 + 判定标准)
  → 代码(添加 Phase 实现)
  → @build0 Error(s), 0 Warning(s)
  → 烧录验证(串口观察 PASS/FAIL
  → 根据实际结果修正判定标准
  → 更新文档(保持同步)
  → 下一阶段

附录:本项目的测试文件清单

文件 作用
test/ch395f_test.h 测试宏、类型定义、Phase 使能开关
test/ch395f_test_task.h 导出 StartCh395fTestTask
test/ch395f_test_task.c 全 10 个 Phase 实现
docs/CH395F_Test_Guide.md 测试规范、用例表格、通过/失败标准
docs/CH395F_Trap_Records.md 已知硬件/软件陷阱及修复