xt-sdk 文档 v1.0.3
xt-sdk 嵌入式 SDK API 参考
载入中...
搜索中...
未找到
xt_unity - Unity 测试框架

版本:2.6.1 | 日期:2026-07-03 | 路径: components/xt_unity/


一、概述

xt_unity 集成了 ThrowTheSwitch.org 的 Unity 测试框架(v2.6.1),为 xt-sdk 提供 C 语言单元测试能力。Unity 是一个轻量级、无依赖的测试框架,适合嵌入式和资源受限环境。xt-sdk 在此基础上增加了 平台适配层 ( xt_unity_port.h ),将 Unity 的输出重定向到 xt_printf ,使其与 xt-sdk 的日志系统无缝集成。

1.1 三层架构

层级 头文件 @defgroup 职责
公共 API unity.h unity(Unity 测试框架) TEST_ASSERT_* 宏、 setUp / tearDown 、 RUN_TEST
内部实现 unity_internals.h unity_internals(Unity 内部实现) 类型自动探测、 setjmp / longjmp 中断机制、断言函数
平台适配 xt_unity_port.h xt_unity_port(Unity 平台适配) 输出重定向到 xt_printf ,256 字节行缓冲

1.2 设计原则

  • **零外部依赖**:Unity 核心不依赖任何第三方库,仅需标准 C 头文件
  • **自动类型探测**:通过 UINT_MAX / ULONG_MAX / UINTPTR_MAX 自动推断整数和指针宽度
  • **setjmp 中断**:测试失败时通过 longjmp 跳出当前测试函数,不依赖信号或异常
  • **宏驱动**:所有断言均为宏,自动捕获 __LINE__ 和文件名

1.3 依赖

依赖 用途
xtiny.h xt_printf 输出(通过 xt_unity_port.h )
setjmp.h 测试失败中断(可用 UNITY_EXCLUDE_SETJMP_H 关闭)
math.h 浮点 NaN/Inf 检测(可用 UNITY_EXCLUDE_MATH_H 关闭)
stdint.h 定长整数类型(可用 UNITY_EXCLUDE_STDINT_H 关闭)

二、核心概念

2.1 测试生命周期与 setjmp/longjmp

Unity 使用 setjmp / longjmp 实现测试失败时的非局部跳转。每个测试函数在执行前通过 TEST_PROTECT() (即 setjmp )设置恢复点,断言失败时调用 TEST_ABORT() (即 longjmp )跳回 UnityDefaultTestRun ,继续执行下一个测试。

UnityBegin()
├── RUN_TEST(test_func_1)
│ ├── setjmp(Unity.AbortFrame) ← 保护点
│ ├── setUp()
│ ├── test_func_1() ← 断言失败 → longjmp
│ └── tearDown()
├── RUN_TEST(test_func_2)
│ ├── setjmp(Unity.AbortFrame)
│ ├── setUp()
│ ├── test_func_2()
│ └── tearDown()
└── UnityEnd() ← 返回失败计数

若定义了 UNITY_EXCLUDE_SETJMP_H ,则 TEST_PROTECT() 恒返回 1, TEST_ABORT() 变为 return —— 此时断言失败后测试函数会继续执行后续语句,可能导致级联错误。

2.2 输出重定向机制

Unity 默认通过 putchar 输出到 stdout。 xt_unity_port.h 将输出重定向到 xt-sdk 的日志系统:

这种 行缓冲 设计避免了逐字符调用 xt_printf 的性能开销,同时确保测试输出与 xt-sdk 日志格式一致。

2.3 整数宽度自动探测

unity_internals.h 按以下优先级探测整数宽度:

  1. 检查 UINT_MAX → 推断 UNITY_INT_WIDTH (16/32/64)
  2. 检查 ULONG_MAX → 推断 UNITY_LONG_WIDTH
  3. 检查 UINTPTR_MAX → 推断 UNITY_POINTER_WIDTH

若任一头文件被排除( UNITY_EXCLUDE_STDINT_H / UNITY_EXCLUDE_LIMITS_H ),则回退到默认值 32 位。用户也可通过 UNITY_INT_WIDTH 、 UNITY_LONG_WIDTH 、 UNITY_POINTER_WIDTH 显式覆盖。

64 位支持由 UNITY_LONG_WIDTH == 64 或 UNITY_POINTER_WIDTH == 64 自动触发,或通过 UNITY_SUPPORT_64 强制启用。

2.4 断言宏的分层设计

Unity 的断言宏分为三层:

层级 命名模式 说明
用户层 TEST_ASSERT_EQUAL_INT(a, b) 自动捕获 __LINE__ ,无自定义消息
消息层 TEST_ASSERT_EQUAL_INT_MESSAGE(a, b, msg) 自动捕获 __LINE__ ,带自定义消息
内部层 UNITY_TEST_ASSERT_EQUAL_INT(a, b, line, msg) 需手动传 line ,由上层宏调用

用户只需使用 TEST_ASSERT_* 或 TEST_ASSERT_*_MESSAGE 系列。内部层宏不应直接调用。

2.5 Shorthand 模式

TEST_ASSERT_EQUAL(expected, actual) 的行为取决于编译时定义的 shorthand 模式:

宏定义 比较方式 适用场景
UNITY_SHORTHAND_AS_OLD (默认) 整数比较 兼容旧代码
UNITY_SHORTHAND_AS_INT 整数比较, NOT_EQUAL 报错 严格类型
UNITY_SHORTHAND_AS_MEM 内存比较 结构体比较
UNITY_SHORTHAND_AS_RAW == 直接比较 任意类型
UNITY_SHORTHAND_AS_NONE 禁用 shorthand 强制使用类型明确的断言

不定义任何 shorthand 宏时,默认使用 UNITY_SHORTHAND_AS_OLD 。


三、API 参考

以下为函数签名大纲,完整的参数说明、返回值、注意事项请查看 Unity 测试框架 。 内部实现细节请查看 unity_internals(Unity 内部实现) ,平台适配接口请查看 xt_unity_port(Unity 平台适配) 。

3.1 测试控制

宏签名 说明
TEST_PASS() 通过当前测试
TEST_FAIL() 标记当前测试失败
TEST_IGNORE() 忽略当前测试(跳过)
TEST_PRINTF(message, ...) 输出格式化消息

3.2 基本断言

宏签名 说明
TEST_ASSERT(condition) 断言条件为真
TEST_ASSERT_TRUE(condition) 断言为 true
TEST_ASSERT_FALSE(condition) 断言为 false
TEST_ASSERT_NULL(pointer) 断言为 NULL
TEST_ASSERT_NOT_NULL(pointer) 断言非 NULL
TEST_ASSERT_EMPTY(pointer) 断言为空
TEST_ASSERT_NOT_EMPTY(pointer) 断言非空

3.3 相等断言

宏签名 说明
TEST_ASSERT_EQUAL_INT(expected, actual) 整数相等(含 INT8/16/32/64 变体)
TEST_ASSERT_EQUAL_UINT(expected, actual) 无符号整数相等(含 UINT8/16/32/64 变体)
TEST_ASSERT_EQUAL_HEX(expected, actual) 十六进制相等(含 HEX8/16/32/64 变体)
TEST_ASSERT_EQUAL_CHAR(expected, actual) 字符相等
TEST_ASSERT_BITS(mask, expected, actual) 位掩码相等

3.4 比较断言

宏签名 说明
TEST_ASSERT_GREATER_THAN(threshold, actual) 大于(含各类型变体)
TEST_ASSERT_LESS_THAN(threshold, actual) 小于(含各类型变体)
TEST_ASSERT_GREATER_OR_EQUAL(threshold, actual) 大于等于(含各类型变体)

3.5 运行器

函数签名 说明
void setUp(void) 每个测试前调用(用户实现)
void tearDown(void) 每个测试后调用(用户实现)
void UNITY_BEGIN(void) 开始测试套件
int UNITY_END(void) 结束测试套件,返回失败计数
void RUN_TEST(func) 运行单个测试函数

四、编译配置

宏 默认 说明
UNITY_EXCLUDE_STDINT_H 未定义 排除 stdint.h ,回退默认宽度
UNITY_EXCLUDE_LIMITS_H 未定义 排除 limits.h ,回退默认宽度
UNITY_EXCLUDE_SETJMP_H 未定义 排除 setjmp ,失败后继续执行
UNITY_EXCLUDE_FLOAT 未定义 禁用浮点断言
UNITY_INCLUDE_DOUBLE 未定义 启用 double 断言
UNITY_EXCLUDE_FLOAT_PRINT 未定义 禁用浮点值打印(减小体积)
UNITY_SUPPORT_64 自动 强制启用 64 位支持
UNITY_INCLUDE_PRINT_FORMATTED 未定义 启用 TEST_PRINTF
UNITY_INCLUDE_EXEC_TIME 未定义 启用测试执行时间统计
UNITY_SHORTHAND_AS_* AS_OLD TEST_ASSERT_EQUAL 的比较方式
XT_UNITY_OUTPUT_BUF_SIZE 256 平台适配层行缓冲大小

五、常见模式

5.1 编写测试文件

#include "unity.h"
// 必须实现,即使为空
void setUp(void) {
// 每个测试前调用
}
void tearDown(void) {
// 每个测试后调用
}
// 测试函数
void test_ring_buffer_write_read(void) {
uint8_t buf[16];
struct xt_rb rb;
xt_rb_init(&rb, buf, sizeof(buf));
xt_rb_putc(&rb, 0xAA);
xt_rb_putc(&rb, 0x55);
uint8_t ch;
TEST_ASSERT_FALSE(xt_rb_getc(&rb, &ch)); // 缓冲区已空
}
void test_crc16_modbus_known_value(void) {
uint8_t data[] = { 0x01, 0x03, 0x00, 0x00, 0x00, 0x0A };
uint16_t crc = xt_crc16_modbus(data, sizeof(data));
}
#define TEST_ASSERT_EQUAL_UINT8(expected, actual)
#define TEST_ASSERT_EQUAL_UINT16(expected, actual)
#define TEST_ASSERT_TRUE(condition)
void setUp(void)
void tearDown(void)
#define TEST_ASSERT_FALSE(condition)
uint16_t xt_crc16_modbus(uint8_t *data, uint16_t length)
void xt_rb_init(struct xt_rb *rb, uint8_t *buf, uint32_t size)
初始化环形缓冲区
void xt_rb_putc(struct xt_rb *rb, uint8_t ch)
写入单个字节
bool xt_rb_getc(struct xt_rb *rb, uint8_t *ch)
读取单个字节
环形缓冲区结构体
定义 xt_rb.h:43

5.2 编写测试 runner

#include "unity.h"
void setUp(void);
void tearDown(void);
void test_ring_buffer_write_read(void);
void test_crc16_modbus_known_value(void);
int main(void) {
RUN_TEST(test_ring_buffer_write_read);
RUN_TEST(test_crc16_modbus_known_value);
return UNITY_END();
}
#define RUN_TEST(func)
#define UNITY_END()
#define UNITY_BEGIN()

UNITY_BEGIN() 初始化测试框架并记录文件名, RUN_TEST() 执行单个测试函数(自动调用 setUp/tearDown), UNITY_END() 输出汇总并返回失败计数——该返回值可作为 main 的退出码。

5.3 浮点近似比较

void test_float_within(void) {
float actual = 3.14159f;
TEST_ASSERT_FLOAT_WITHIN(0.001f, 3.14159f, actual); // 差值在 0.001 内
TEST_ASSERT_EQUAL_FLOAT(3.14159f, actual); // 使用默认精度 0.00001
}
#define TEST_ASSERT_EQUAL_FLOAT(expected, actual)
#define TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual)

5.4 数组与内存比较

void test_array_equal(void) {
uint16_t expected[] = { 0x0001, 0x0002, 0x0003 };
uint16_t actual[] = { 0x0001, 0x0002, 0x0003 };
TEST_ASSERT_EQUAL_UINT16_ARRAY(expected, actual, 3);
TEST_ASSERT_EQUAL_MEMORY(expected, actual, sizeof(expected));
}
#define TEST_ASSERT_EQUAL_MEMORY(expected, actual, len)
#define TEST_ASSERT_EQUAL_UINT16_ARRAY(expected, actual, num_elements)

5.5 忽略测试与自定义消息

void test_not_implemented_yet(void) {
TEST_IGNORE_MESSAGE("TODO: implement when driver is ready");
}
void test_with_context(void) {
int result = compute();
TEST_ASSERT_EQUAL_INT_MESSAGE(42, result, "compute() should return 42 for default config");
}
#define TEST_ASSERT_EQUAL_INT_MESSAGE(expected, actual, message)
#define TEST_IGNORE_MESSAGE(message)

六、反模式

反模式 问题 正确做法
未实现 setUp / tearDown 链接错误 即使为空也必须实现这两个函数
直接调用 UNITY_TEST_ASSERT_* 内部宏 绕过 __LINE__ 捕获,行号错误 始终使用 TEST_ASSERT_* 用户层宏
关闭 setjmp 后不检查返回值 断言失败后继续执行,级联错误 保持 setjmp 开启,或失败后立即 return
在 setUp 中调用 TEST_ASSERT_* setjmp 保护点尚未设置,行为未定义 setUp 中只做初始化,不做断言
浮点断言使用 == 而非 TEST_ASSERT_EQUAL_FLOAT 浮点精度问题导致误判 使用 TEST_ASSERT_FLOAT_WITHIN 或 TEST_ASSERT_EQUAL_FLOAT
未定义 UNITY_SHORTHAND_AS_* 就用 TEST_ASSERT_EQUAL 默认 AS_OLD 做整数比较,对结构体/指针无意义 明确定义 shorthand 模式,或使用类型明确的断言
UNITY_END() 返回值被忽略 CI 中测试失败不报错 return UNITY_END(); 作为 main 退出码