C语言单元测试框架CuTest设计与实现详解
1. C语言单元测试框架CuTest设计与实现
1.1 框架概述
CuTest是一款轻量级C语言单元测试框架,整个框架仅包含两个文件:CuTest.c和CuTest.h,总代码量不足1000行。该框架实现了完整的单元测试功能,包括:
- 多种类型的断言机制
- 测试用例(Test Case)管理
- 测试套件(Test Suite)组织
- 测试结果汇总与报告
2. 核心架构设计
2.1 断言机制实现
CuTest提供了多种类型的断言宏,用于比较测试值与期望值:
#define CuAssertStrEquals(tc,ex,ac) CuAssertStrEquals_LineMsg((tc),__FILE__,__LINE__,NULL,(ex),(ac)) #define CuAssertIntEquals(tc,ex,ac) CuAssertIntEquals_LineMsg((tc),__FILE__,__LINE__,NULL,(ex),(ac)) #define CuAssertDblEquals(tc,ex,ac,dl) CuAssertDblEquals_LineMsg((tc),__FILE__,__LINE__,NULL,(ex),(ac),(dl)) #define CuAssertPtrEquals(tc,ex,ac) CuAssertPtrEquals_LineMsg((tc),__FILE__,__LINE__,NULL,(ex),(ac))以整数比较断言CuAssertIntEquals为例,其底层实现如下:
void CuAssertIntEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message, int expected, int actual) { char buf[STRING_MAX]; if (expected == actual) return; sprintf(buf, "expected <%d> but was <%d>", expected, actual); CuFail_Line(tc, file, line, message, buf); }当测试失败时,框架会收集以下信息:
- 测试文件路径
- 失败行号
- 用户自定义错误消息
- 期望值与实际值
2.2 错误处理机制
CuTest采用setjmp/longjmp实现错误处理流程:
void CuFailInternal(CuTest* tc, const char* file, int line, CuString* string) { char buf[HUGE_STRING_LEN]; sprintf(buf, "%s:%d: ", file, line); CuStringInsert(string, buf, 0); tc->failed = 1; tc->message = string->buffer; if (tc->jumpBuf != 0) longjmp(*(tc->jumpBuf), 0); }错误处理流程:
- 拼接完整的错误消息字符串
- 设置测试用例失败标志(tc->failed)
- 保存错误消息到测试用例结构体
- 通过longjmp跳转到测试执行点
3. 测试组织与管理
3.1 测试用例定义
测试用例函数原型为:
typedef void (*TestFunction)(CuTest *);示例测试用例:
void test_add(CuTest* tc) { CuAssert(tc, "test not pass", 2 == AddInt(1, 0)); }3.2 测试套件实现
测试套件用于组织相关测试用例:
CuSuite* TestAdd(void) { CuSuite* suite = CuSuiteNew(); SUITE_ADD_TEST(suite, test_add); return suite; }测试套件内部使用数组管理测试用例:
void CuSuiteAdd(CuSuite* testSuite, CuTest *testCase) { assert(testSuite->count < MAX_TEST_CASES); testSuite->list[testSuite->count] = testCase; testSuite->count++; }3.3 多级测试套件组织
支持将多个测试套件合并为更大的测试套件:
CuSuite* suite = CuSuiteNew(); CuSuiteAddSuite(suite, CuGetSuite()); CuSuiteAddSuite(suite, CuStringGetSuite()); CuSuiteAddSuite(suite, TestAdd());4. 测试执行流程
4.1 测试运行机制
测试套件执行流程:
void CuSuiteRun(CuSuite* testSuite) { int i; for (i = 0; i < testSuite->count ; ++i) { CuTest* testCase = testSuite->list[i]; CuTestRun(testCase); if (testCase->failed) { testSuite->failCount += 1; } } }单个测试用例执行流程:
void CuTestRun(CuTest* tc) { jmp_buf buf; tc->jumpBuf = &buf; if (setjmp(buf) == 0) { tc->ran = 1; (tc->function)(tc); } tc->jumpBuf = 0; }4.2 测试结果汇总
测试执行完成后生成汇总报告:
void RunAllTests(void) { CuString *output = CuStringNew(); CuSuite* suite = CuSuiteNew(); CuSuiteAddSuite(suite, TestAdd()); CuSuiteRun(suite); CuSuiteSummary(suite, output); CuSuiteDetails(suite, output); printf("%s\n", output->buffer); }5. 完整使用示例
5.1 定义测试用例
void test_add(CuTest* tc) { CuAssert(tc, "test not pass", 2 == 1 + 1); }5.2 创建测试套件
CuSuite* TestAdd(void) { CuSuite* suite = CuSuiteNew(); SUITE_ADD_TEST(suite, test_add); return suite; }5.3 执行测试
int main() { RunAllTests(); getchar(); return 0; }6. 设计特点分析
- 极简设计:核心功能仅需两个文件,便于集成到项目中
- 零依赖:不依赖任何外部库,纯C语言实现
- 灵活的组织结构:支持多级测试套件,适合大型项目
- 详细的错误报告:提供文件、行号等调试信息
- 跨平台:基于标准C语言特性,可移植性强
CuTest框架虽然小巧,但完整实现了单元测试所需的核心功能,特别适合嵌入式系统和资源受限环境下的C语言项目测试需求。
