C代码单元测试框架
如何在Windows本地集成unity和CMocka单元测试框架?
一、Cmocka源码下载编译
官方网站 https://cmocka.org/ 下载源码。
CMocka 使用 CMake 来管理构建过程,它能自动生成适用于 MinGW 的构建文件(MinGW Makefiles
)。如果你想要在 Windows 上使用MinGW 编译 CMocka则需要用到CMake工具。
CMake 是一个构建系统生成工具,它的作用是帮助你生成适用于不同平台的构建文件,如: Makefile。
MinGW 是一个编译工具链,提供了 GCC 编译器等工具,用于在 Windows 上编译 C、C++ 等程序。
二、下载和安装 MinGW
1、下载:访问 MinGW官方网站。 Download File List - MinGW - Minimalist GNU for Windows - OSDN
2、安装配置
安装时,确保选择以下组件(选择后点击apply应用继续安装):
(1)、mingw32-base
:基础开发工具。
(2)、mingw32-gcc-g++
:C 和 C++ 编译器。
(3)、(可选)mingw32-msys-base
:如果你希望使用类似 Unix 的环境,可以选择安装。
3、设置环境变量
安装完成后,你需要将 MinGW 的 bin
目录添加到系统的环境变量 中,这样你就可以在任何位置使用 MinGW 编译工具。
(1)、右键点击“此电脑”或“计算机”,选择“属性”。
(2)、点击“高级系统设置”,然后选择“环境变量”。
(3)、在 系统变量 中找到 Path
,点击“编辑”。
(4)、在编辑窗口中,点击“新建”并添加 MinGW 安装路径下的 bin
目录,例如:C:\MinGW\bin
(根据你实际的安装路径)。
(5)、保存并退出。
4、测试安装成功
(1)、打开 命令提示符(cmd
)。
(2)、输入 gcc --version 命令来检查 GCC 是否安装成功
三、下载和安装 CMake
1、下载 CMake:访问 CMake 的 下载页面,并下载适合 Windows 的安装包。 Download CMake
安装时可以选择 Add CMake to system PATH(将 CMake 添加到系统 PATH),这样你可以在命令行中直接使用 cmake
命令。
(1)、cmake --version (如果找不到可以尝试重新开启cmd命令提示框)
四、使用 CMake 构建 CMocka
1、进入你下载的 CMocka 源代码目录:
cd path/to/cmocka
2、创建一个新的构建目录:
mkdir build
cd build
3、使用 CMake 配置构建过程:
cmake ..
(1)、如果你希望指定 MinGW 编译器,可以使用 -G
选项来告诉 CMake 使用 MinGW 构建。例如:
cmake -G "MinGW Makefiles" ..
这将告诉CMake使用MinGW的make工具来生成Makefile。
4、开始构建 CMocka:
make
5、开始安装 CMocka:
make install
6、常见错误提示
提示:CMake Error at cmake_install.cmake:41 (file): file cannot create directory: C:/Program Files (x86)/cmocka/lib/pkgconfig. Maybe need administrative privileges.
此时需要已管理员方式运行你的终端,windows下可以在搜索框中搜索cmd,在弹出的运用程序中选择以管理员方式运行。
7、如果构建成功,CMocka 的库文件将被生成。
库文件生成目录:文件将被安装到 C:/Program Files (x86)/cmocka/
目录中。
五、验证CMocka是否安装成功
构建和安装完成后,你可以通过以下步骤验证 CMocka 是否安装成功。
1、在终端中检查 CMocka 库是否被安装到正确的位置。
2、编译运行测试程序
(1)、创建一个 test_example.c
文件:
#include <cmocka.h> static void test_example(void **state) { (void) state; // unused variable assert_int_equal(1, 1); // Example test: asserts that 1 == 1 } int main(void) { const struct CMUnitTest tests[] = { cmocka_unit_test(test_example), }; return cmocka_run_group_tests(tests, NULL, NULL); }
(2)、用 MinGW 编译并运行测试(windows搜索框中搜索cmd,以管理员方式运行):
gcc -o test_example test_example.c -lcmocka
也能写makefile自动编译运行
# Makefile for testing with CMocka # 编译器和编译选项 CC = gcc # CMocka 的头文件路径 CFLAGS = -Wall -std=c99 -I"C:/Program Files (x86)/cmocka/include" # CMocka 的库文件路径 LDFLAGS = -L"C:/Program Files (x86)/cmocka/lib" -lcmocka # 目标可执行文件 TARGET = test_program.exe # 源文件 #SRCS = test_program.c # 自动查找当前目录下的所有 .c 文件 不仅是test_program.c文件,还包含了库里的其它C文件 SRCS = $(wildcard *.c) # 目标文件 OBJS = $(SRCS:.c=.o) # 默认目标 all: $(TARGET) # 编译可执行文件 $(TARGET): $(OBJS) $(CC) $(OBJS) $(LDFLAGS) -o $(TARGET) # 编译源文件 %.o: %.c $(CC) $(CFLAGS) -c $< -o $@ # 清理目标 # Windows 删除命令 clean: rm -f $(OBJS) $(TARGET) # 运行程序 run: $(TARGET) # 运行生成的 .exe 文件 ./$(TARGET)
3、常见错误提示
(1)、提示找不到头文件时使用绝对路劲:gcc -I"C:/Program Files (x86)/cmocka/include" -L"C:/Program Files (x86)/cmocka/lib" -o test_example test_example.c -lcmocka
(2)、使用绝对路劲如果提示cmocka.h下的各种错误,证明测试程序已经能找到cmocka.h了,此时我们进入C:/Program Files (x86)/cmocka/include下找到cmocka.h,打开源代码发现提示使用时必须包含提供的头文件。在源代码头部添加后再编译就没有报错了。
(3)、由于找不到cmocka.dll,无法继续执行代码。重新安装程序可能会解决此问题。
但我们查询到这个文件在C:\Program Files (x86)\cmocka\bin 目录下,将此路径添加到系统环境变量path下即可。
4、运行测试程序:
编译成功会在同路劲下生成 test_example.exe文件,在cmd下运行命令:test_example.exe ,也可以双击运行。
windows运行:test_example.exe linux:./test_example
如果使用makefile文件编译,我们可以在cmd命令框输入make编译,make run运行编译后的.exe程序,make clean来清空编译生成的.o和.exe文件。
成功运行如下:
六、下载 Unity 测试框架源代码
只需unity.c unity.h unity_internals.h即可
下载:访问 Unity GitHub 页面。
(1)、点击 "Code" 按钮,然后选择 "Download ZIP" 进行下载。
(2)、解压缩下载的文件到你选择的目录。
七、在CMocka的工程上使用unity——unity集成CMocka
1、首先将下载的unity源文件放到我们的工程路劲
2、在test_example.c文件下包含unity.h头文件
make
提示错误:找不到unity.h头文件,我们修改makefile,增加unity.h放置的绝对路劲,如下:
# Makefile for testing with CMocka # 编译器和编译选项 CC = gcc # CMocka 的头文件路径 CFLAGS = -Wall -std=c99 -I"C:/Program Files (x86)/cmocka/include" -I D:/CMocka/test/unity_cmocka_test # CMocka 的库文件路径 LDFLAGS = -L"C:/Program Files (x86)/cmocka/lib" -lcmocka # 目标可执行文件 TARGET = test_program.exe # 源文件 #SRCS = test_program.c # 自动查找当前目录下的所有 .c 文件 SRCS = $(wildcard *.c) # 目标文件 OBJS = $(SRCS:.c=.o) # 默认目标 all: $(TARGET) # 编译可执行文件 $(TARGET): $(OBJS) $(CC) $(OBJS) $(LDFLAGS) -o $(TARGET) # 编译源文件 %.o: %.c $(CC) $(CFLAGS) -c $< -o $@ # 清理目标 # Windows 删除命令 clean: rm -f $(OBJS) $(TARGET) # 运行程序 run: $(TARGET) # 运行生成的 .exe 文件 ./$(TARGET)
3、执行编译指令:make
报错如下:证明已经能正确找到unity.h头文件
unity.o:unity.c:(.text+0x26e4): undefined reference to `setUp'
unity.o:unity.c:(.text+0x26fe): undefined reference to `tearDown'
这两个接口需要我们自己实现在主函数里,这是两个回调函数,执行测试用例前后会自动调用。执行前就可以用CMocka模拟参数传入测试用例了。
unity单元测试示例:
1 #include <stdarg.h> 2 #include <stddef.h> 3 #include <stdint.h> 4 #include <setjmp.h> 5 #include <stdint.h> 6 7 #include <cmocka.h> 8 9 #include <unity.h> 10 11 12 // 被测试的函数(示例) 13 int add(int a, int b) { 14 return a + b; 15 } 16 17 // 单元测试函数1 18 void test_add_two_positive_numbers(void) { 19 TEST_ASSERT_EQUAL_INT(5, add(2, 3)); // 断言 2 + 3 等于 5 5是预期的结果 2 和 3 是传入的结果,如果传入的参数运行结果跟预期结果不一致,就会产生断言 20 } 21 // 单元测试函数2 22 void test_add_negative_and_positive(void) { 23 TEST_ASSERT_EQUAL_INT(10, add(3, 6)); // 断言 3 + 6 = 9 这个运行就会产生断言 运行结果与预期值不符 24 } 25 26 // setUp 函数在每个测试用例之前执行(回调函数) 每个测试用例执行前都会执行这个函数 27 void setUp(void) { 28 // 初始化工作,例如分配内存、初始化变量等 29 } 30 31 // tearDown 函数在每个测试用例之后执行(回调函数) 测试用例产生断言就会执行这个函数 32 void tearDown(void) { 33 // 清理工作,例如释放内存、重置状态等 34 } 35 36 static void test_example(void **state) { 37 (void) state; // unused variable 38 assert_int_equal(1, 1); // Example test: asserts that 1 == 1 39 } 40 41 42 int main(void) { 43 /*unity*/ 44 UNITY_BEGIN();//初始化unity测试框架 45 RUN_TEST(test_add_two_positive_numbers);//运行unity单元测试 46 RUN_TEST(test_add_negative_and_positive);//运行unity单元测试 47 UNITY_END();//结束测试 48 49 /*CMocka*/ 50 const struct CMUnitTest tests[] = { 51 cmocka_unit_test(test_example), 52 }; 53 54 return cmocka_run_group_tests(tests, NULL, NULL); 55 }
4、CMocka集成到unity下成功运行如下
5、unity扩展用法
(1)、自定义断言
//自定义断言 断言返回的数据在参数1 2 范围内 #define TEST_ASSERT_FLOAT_WITHIN_MY(expected1, expected2, actual) TEST_ASSERT_TRUE((expected1 < actual) && (actual < expected2)?1:0) void test_float_out_range(void) { TEST_ASSERT_FLOAT_WITHIN_MY(0.0, 100.0, float_add(3.14,99.14)); } //自定义断言 断言一个浮动值在一个给定的误差范围内 #define TEST_ASSERT_FLOAT_OUT_RANGE(expected, tolerance, actual) TEST_ASSERT_TRUE(fabs((actual) - (expected)) < (tolerance)) void test_float_within_tolerance(void) { float tolerance = 30.0; TEST_ASSERT_FLOAT_OUT_RANGE(80.0, tolerance, 100.0); }
(2)、测试套件
可以创建多个测试套件。通过这种方式,你可以组织并运行一组测试,而不必每次都单独调用每个测试函数。
// 将多个测试函数添加到一个测试套件中 void test_suite(void) {//同组测试套件可以是相关功能的几个测试函数,参数可以关联起来 RUN_TEST(test_add_two_positive_numbers); RUN_TEST(test_add_negative_and_positive); } int main(void) { UNITY_BEGIN(); // 开始测试 test_suite(); // 运行测试套件 return UNITY_END(); // 结束测试 }
(3)、性能测试
通过记录函数执行的时间,来测量代码的性能
#include "unity.h" #include "timer.h" // 假设你有一个计时器库 void test_performance_of_function(void) { uint32_t start_time = timer_get_current_time(); // 调用需要测试性能的函数 some_function_to_test(); uint32_t end_time = timer_get_current_time(); uint32_t elapsed_time = end_time - start_time; // 假设你有一个性能限制,比如函数执行时间不能超过 100ms TEST_ASSERT_TRUE(elapsed_time < 100); }
(4)、跳过某些测试
某些情况下可能不希望运行某些测试,比如依赖于外部硬件的测试。Unity 提供了一个 TEST_IGNORE()
宏来跳过某个测试。
void test_that_is_skipped(void)
{
TEST_IGNORE(); // 跳过此测试 // 这部分代码永远不会执行
}
(5)、测试结果输出控制
可以通过 UNITY_OUTPUT_CHAR()
和 UNITY_OUTPUT_INT()
等宏自定义输出方式。你可以把测试结果输出到不同的地方,例如文件、串口、或通过调试端口。
八、强制为现有目录启用 8.3 文件名
有时我们在输入C:\Program Files (x86)
这种文件名时,因为有空格,有些编译器识别不了,需要其它操作,windows下这类文件目录对应一个短文件名,如何使用呢?
你可以使用 fsutil
工具来强制更新现有目录的 8.3 文件名。按以下步骤操作:
1、打开 命令提示符(以管理员身份运行)。
2、输入以下命令以强制重新生成 C:\Program Files (x86)
目录下的 8.3 文件名:
fsutil 8dot3name set C:\Program Files (x86) 0
使用 dir /x命令
完成上述步骤后,运行 dir /x
命令查看目录内容时,应该可以看到 8.3 文件名了。例如:
虽然 Progra~2
是 Program Files (x86)
的短路径表示法,但并不是所有的工具或者环境都能够正确解析这种表示法。如果还是发现引用不到路劲中的头文件,就使用“path/./..”方法。