為什麼選 CUnit?
- 純 C 實現,零依賴,嵌入式/跨平台友善。
- 提供豐富斷言宏(`CU_ASSERT`、`CU_ASSERT_EQUAL`、`CU_ASSERT_PTR_NOT_NULL`...)。
- 支援四種執行模式:Automated (XML)、Basic (文本)、Console (互動)、Curses (終端 GUI)。
- 結構清晰:Registry → Suite(s) → Test(s),映射專案模組化結構。
架構模型
Test Registry (單例,進程僅一個)
│
├─ Suite "ModuleA" ← init/cleanup 可選
│ ├─ Test "test_add"
│ ├─ Test "test_sub"
│ └─ ...
├─ Suite "ModuleB"
│ └─ ...
└─ ...
核心 API 流程
CU_initialize_registry():初始化註冊表,回傳 `CUE_SUCCESS` 或錯誤碼。
CU_add_suite(name, init_fn, cleanup_fn):新增測試套件,回傳 `CU_pSuite`。
CU_add_test(suite, name, test_fn):新增測試用例,函式簽名 `void test_fn(void)`。
- 選擇執行介面:
CU_automated_run_tests():輸出 XML,適合 CI/CD。
CU_basic_run_tests():標準輸出摘要。
CU_console_run_tests():互動式選單。
CU_cleanup_registry():釋放資源,回傳錯誤碼。
完整範例:測試 `sum(int a, int b)`
被測代碼 (math.h / math.c)
/* math.h */
#ifndef MATH_H
#define MATH_H
int sum(int a, int b);
#endif
/* math.c */
#include "math.h"
int sum(int a, int b) { return a + b; }
測試代碼 (test_math.c)
#include <CUnit/Basic.h>
#include "math.h"
/* --- 測試用例函式 --- */
void test_sum_positive(void) {
CU_ASSERT_EQUAL(sum(3, 4), 7);
}
void test_sum_negative(void) {
CU_ASSERT_EQUAL(sum(-5, 3), -2);
}
void test_sum_zero(void) {
CU_ASSERT_EQUAL(sum(0, 0), 0);
}
void test_sum_overflow(void) {
/* 有號溢位屬 UB,此處僅示範斷言寫法 */
CU_ASSERT(sum(INT_MAX, 0) == INT_MAX);
}
/* --- Suite 初始化/清理 (可選) --- */
int init_suite(void) { return 0; } // 分配共享資源
int clean_suite(void) { return 0; } // 釋放共享資源
/* --- 註冊入口 --- */
int register_math_tests(void) {
CU_pSuite suite = CU_add_suite("MathSuite", init_suite, clean_suite);
if (!suite) return CU_get_error();
if (!CU_add_test(suite, "test_sum_positive", test_sum_positive) ||
!CU_add_test(suite, "test_sum_negative", test_sum_negative) ||
!CU_add_test(suite, "test_sum_zero", test_sum_zero) ||
!CU_add_test(suite, "test_sum_overflow", test_sum_overflow)) {
return CU_get_error();
}
return CUE_SUCCESS;
}
測試執行主程式 (test_runner.c)
#include <CUnit/Automated.h>
#include <CUnit/Basic.h>
#include <CUnit/Console.h>
#include <stdio.h>
extern int register_math_tests(void);
int main(void) {
if (CU_initialize_registry() != CUE_SUCCESS) return CU_get_error();
if (register_math_tests() != CUE_SUCCESS) {
CU_cleanup_registry();
return CU_get_error();
}
/* 1. Automated → XML 報告 (CI 友善) */
CU_set_output_filename("test_report");
CU_list_tests_to_file();
CU_automated_run_tests();
/* 2. Basic → 控制台摘要 */
CU_basic_set_mode(CU_BRM_VERBOSE);
CU_basic_run_tests();
/* 3. Console → 互動模式 (手動除錯用) */
// CU_console_run_tests();
CU_cleanup_registry();
return CU_get_error();
}
建置與執行
# Makefile
CC = gcc
CFLAGS = -std=c99 -Wall -Wextra -I/usr/local/include
LDFLAGS = -L/usr/local/lib -lcunit
all: test_runner
test_runner: math.o test_math.o test_runner.o
$(CC) $^ -o $@ $(LDFLAGS)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -f *.o test_runner test_report.xml