cmocka
- cmocka交叉编译
- 源码下载
- 编译准备
- 源码修改
- 指定编译器
- 编译
- cmocka使用示例
- 常见问题
- 参考
单元测试框架是一个软件包,它能够让开发者比较方便的表达产品代码需要表现出什么样的行为。单元测试框架提供了一个自动化单元测试的解决方案,让开发者把更多的精力放在测试用例的设计的编写上,而不用花精力考虑如何对测试用例进行组织。
cmocka是一个优雅的C语言单元测试框架,支持模拟对象。它只需要标准的C库,适用于各种计算平台(Linux、windows,以及嵌入式)。
理论上来说,cmocka可以支持任何使用标准C库的交叉编译器。
本文将介绍如何在嵌入式环境(交叉编译)搭建cmocka单元测试环境,以及cmocka的简单使用示例。
cmocka交叉编译
源码下载
目前最新的1.1.5版本,对于嵌入式环境,我们需要下载源码进行交叉编译
cmocka1.1源码下载地址
这里以cmocka-1.1.5.tar.xz为例。
编译准备
将上述源码在linux环境中解压,并在源码同级目录新建编译目录build_dir

cmocka-1.1.5内容如下:

源码修改
进入cmocka-1.1.5源码目录,修改顶层CMakeLists.txt,将如下行注释掉。
doc组件需要特定的库支持,嵌入式环境一般没有这个库,而且这个只是生成代码注释,对功能没有影响,所以将其注释。
# add_subdirectory(doc)
指定编译器
接下来在build_dir目录的同级目录新建配置文件arm64_setup.cmake(文件名随意)

文件内容如下:
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm64)set(tools /mnt/opt/compile_tools/bin/)
set(CMAKE_C_COMPILER ${tools}/aarch64-openwrt-linux-gcc)
set(CMAKE_CXX_COMPILER ${tools}/aarch64-openwrt-linux-g++)
CMAKE_SYSTEM_NAME指定嵌入式系统类型CMAKE_SYSTEM_PROCESSOR指定嵌入式平台tools交叉编译器路径(以实际路径为准)CMAKE_C_COMPILERC交叉编译器CMAKE_CXX_COMPILERC++交叉编译器
编译
编译需要进入build_dir目录,先执行如下命令生成必要的配置和makefile文件
$ cd build_dir
$ cmake -DCMAKE_TOOLCHAIN_FILE=../arm64_setup.cmake -DBUILD_STATIC_LIB=ON ../cmocka-1.1.5/
-DCMAKE_TOOLCHAIN_FILE是指定刚刚创建的编译器配置文件-DBUILD_STATIC_LIB=ON是编译生成静态库,去掉这句只会生成动态库../cmocka-1.1.5/指定cmocka源码目录
如果上述步骤没有错误,那么在build_dir应该会生成若干目录和文件,其中就包括makefile,接下来执行make 编译即可。
$ make
出现下列提示表示编译成功:
Scanning dependencies of target cmocka
[ 4%] Building C object src/CMakeFiles/cmocka.dir/cmocka.c.o
......
[100%] Linking C executable test_uptime
[100%] Built target test_uptime
如果需要
clean,直接在build_dir目录执行make clean是不行的,因为Cmake不支持。最简单的方式就是把build_dir目录手动清空即可。
编译完成后查看build_dir/src,会生成我们需要的动态库或者静态库.
使用file指令可以看到这个动态库是ARM aarch64平台的,表明我们交叉编译成功了。
$ build_dir/src$ ls
CMakeFiles libcmocka.so libcmocka.so.0.7.0 Makefile
cmake_install.cmake libcmocka.so.0 libcmocka-static.a$ file libcmocka.so.0.7.0
libcmocka.so.0.7.0: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, with debug_info, not stripped
另外,build_dir/example目录下也会生成一些示例demo,可以直接在开发板上运行。
$ build_dir/example$ ls
allocate_module_test CMakeFiles Makefile
assert_macro_test cmake_install.cmake mock
assert_module_test CTestTestfile.cmake simple_test
$ file simple_test
simple_test: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-musl-aarch64.so.1, with debug_info, not stripped
有了libcmocka,在加上源码中的cmocka.h,我就可以利用cmocka提供的API编译我们自己的单元测试代码了。
cmocka.h位于cmocka源码的include目录
cmocka使用示例
测试代码如下:
$ tree hello/
hello/
├── inc
│ └── cmocka.h
├── libs
│ ├── libcmocka.so
│ └── libcmocka-static.a
├── makefile
└── src└── hello_cmocka.c
libs文件夹主要存放库文件,推荐使用libcmocka静态库,使用静态库的好处是编译出的二进制文件在开发板上可以直接运行,如果是动态库还需要开发板上也安装这个动态库,静态库的缺点就是编译产物体积较大。
示例makefile文件如下:
#source file
SOURCE += $(wildcard ./src/*.c)
OBJS := $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCE)))TARGET := hello_cmocka#compile and lib parameter
CC := aarch64-openwrt-linux-gcc
LIBS := -lcmocka-static
LDFLAGS := -L./libs
DEFINES :=
INCLUDE := -I./inc/
CFLAGS := -g -Wno-unused-variable -O3 $(DEFINES) $(INCLUDE)
CXXFLAGS:= $(CFLAGS) .PHONY: all : $(TARGET)
objs : $(OBJS)clean :rm -fr ./src/*.orm -fr $(TARGET)$(TARGET) : $(OBJS)$(CC) $(CXXFLAGS) -o $@ $(OBJS) $(LDFLAGS) $(LIBS)
示例hello_cmocka.c如下:
#include <stdarg.h>
#include <stdio.h>
#include <setjmp.h>
#include <stddef.h>
#include <stdint.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <cmocka.h>static int add(int a ,int b)
{return a+b;
}static char* print_string(int num)
{switch(num){case 1: return "CASE1";case 2: return "CASE2";default: return "NOT SUPPORT";}return "NOT SUPPORT";
}static void test_demo1(void **state)
{int ret = add(3,2); assert_int_equal(ret, 5);(void) state;
}
static void test_demo2(void **state)
{int ret = add(3,2);assert_int_equal(ret, 0);(void) state;
}static void test_demo3(void **state)
{char *p = print_string(1);assert_string_equal(p, "CASE1");(void) state;
}int main(void)
{const struct CMUnitTest tests[] = {cmocka_unit_test(test_demo1),cmocka_unit_test(test_demo2),cmocka_unit_test(test_demo3),};return cmocka_run_group_tests(tests, NULL, NULL);
}
运行结果如下:
# ./hello_cmocka
[==========] Running 3 test(s).
[ RUN ] test_demo1
[ OK ] test_demo1
[ RUN ] test_demo2
[ ERROR ] --- 0x5 != 0
[ LINE ] --- src/hello_cmocka.c:46: error: Failure!
[ FAILED ] test_demo2
[ RUN ] test_demo3
[ OK ] test_demo3
[==========] 3 test(s) run.
[ PASSED ] 2 test(s).
[ FAILED ] 1 test(s), listed below:
[ FAILED ] test_demo21 FAILED TEST(S)
提示test_demo2测试失败,原因是0x5 != 0,test_demo2的预期结果是0,实际返回5,不符合预期,故测试失败。
assert_int_equal()是判断int类型结果,assert_string_equal()是判断const char *类型的结果。除此之外,cmocka还提供了更多其他的测试API,请参考cmocka.h.
常见问题
- 编译错误
/cmocka-1.1.5/include/cmocka.h:132:28: error: conflicting types for 'uintptr_t'
typedef unsigned int uintptr_t;
/mnt/opt/include/bits/alltypes.h:109:24: note: previous declaration of 'uintptr_t' was heretypedef unsigned _Addr uintptr_t;
cmocka源码中uintptr_t定义和我们的交叉编译器中的定义冲突了,这里只需要暂时把编译器中的定义注释掉,编译完cmocka再改回来即可。
- 其他编译问题
cmocka仅使用了标准C库,它的跨平台兼容性很好。因此,对于绝大多数的交叉编译器应该都是支持的(除非你的编译器版本很低,对C库的支持不全)。
因此,交叉编译cmocka源码中遇到的编译问题基本上都是C代码问题,与平台无关。
参考
- cmocka官网
- cmocka API介绍

















