官网: https://cmocka.org/
源码: https://gitlab.com/cmocka/cmocka
CMocka API: CMocka API
CMocka 是一款支持 mock 对象、面向C语言的单元测试框架,CMocka 往往是编译成库的形式,供C单元测试程序链接调用。其前身是谷歌开发的Cmockery,由于后者缺少维护,因此 CMocka 继承了 Cmockery 并继续维护。
CMocka 框架的特性:
CMocka 往往是以库的形式提供给测试程序链接调用的,为此我们需要先将 CMocka 源码编译成库。
从官网下载 CMocka 源码(系统中centos7),解压,然后:
# cd cmocka
# mkdir build && cd build
# cmake -DCMAKE_INSTALL_PREFIX=/usr/lib ..
# make
# make install
Linux 动态库的默认搜索路径是 /lib 和 /usr/lib
源码 cmocka/example/simple_test.c
很好的展示了 CMocka 的通常用法,代码如下所示:
#include
#include
#include
#include
// 在 #include 之前,必须先 #include 上面四个头文件,这是官网 The CMocka API 中明确要求的,在 cmocka.h 头文件开头部分也有注明。
#include
/* null_test_success 函数是测试用例: 什么也没有做,而且成功返回了*/
static void null_test_success(void **state) {
(void) state; /* unused */
}
int main(void) {
// CMUnitTest 结构体是测试用例集(可以包含多个测试用例),每个测试用例可以设定可选的 startup 和 teardown 函数,用于负责执行测试前的初始化和测试后的销毁工作
const struct CMUnitTest tests[] = {
// 用了 cmocka_unit_test 宏来填充 CMUnitTest 结构体中的测试用例( startup 和 teardown 为 NULL)
cmocka_unit_test(null_test_success),
};
// cmocka_run_group_tests 函数用于启动测试并展示测试结果,可以为测试集指定全局的 startup 和 teardown(示例中都是NULL)。
return cmocka_run_group_tests(tests, NULL, NULL);
}
我们来执行它
# cd cmocka/example
# gcc -o simple_test simple_test.c -lcmocka
# ./simple_test
[==========] Running 1 test(s).
[ RUN ] null_test_success
[ OK ] null_test_success
[==========] 1 test(s) run.
[ PASSED ] 1 test(s).
CMocka 的测试用例是一个C函数,其函数签名为:
CMocka 的测试用例是一个C函数,其函数签名为:
其中void **state
指针是指向 CMUnitTest
结构体中的void *initial_state
变量的地址,在初始化 CMUnitTest
结构体时会设置initial_state
变量初始值(如 cmocka_unit_test 宏会设置 initial_state 为 NULL,还有其他初始化的宏,会在后续中介绍),state
指针也会传递给测试用例对应的 setup
和 teardown
函数。
int setup(void **state)
int teardown(void **state)
执行测试用例的过程中,会按先后顺序依次调用setup、test_func 和 teardown
三个函数,它们都可以访问(读或写) CMUnitTest
结构体中的initial_state
变量。
源码cmocka/tests/test_basics.c
中的示例很好的演示了 state 指针的使用,代码如下(有删减):
static int setup(void **state) {
int *answer = malloc(sizeof(int));
assert_non_null(answer);
*answer = 42;
*state = answer;
return 0;
}
static int teardown(void **state) {
free(*state);
return 0;
}
static void int_test_success(void **state) {
int *answer = *state;
assert_int_equal(*answer, 42);
}
int main(void) {
const struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(int_test_success, setup, teardown),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}
除此之外,源码 cmocka/tests/test_fixtures.c
还演示了有关state
指针的更多用法
CMUnitTest 结构体变量用于存放测试用例集,该结构体定义如下:
struct CMUnitTest {
const char *name; /* 测试用例名 */
CMUnitTestFunction test_func; /* 测试用例函数指针 */
CMFixtureFunction setup_func; /* 测试用例对应的setup函数指针 */
CMFixtureFunction teardown_func; /* 测试用例对应的teardown函数指针 */
void *initial_state; /* 测试用例私有数据指针 */
};
CMocka 提供了足够丰富的宏用于初始化 CMUnitTest
结构体,官网API手册中的 Running Tests 模块列出了所有的这些宏定义:
#define cmocka_unit_test(f) { #f, f, NULL, NULL, NULL }
#define cmocka_unit_test_setup(f, setup) { #f, f, setup, NULL, NULL }
#define cmocka_unit_test_teardown(f, teardown) { #f, f, NULL, teardown, NULL }
#define cmocka_unit_test_setup_teardown(f, setup, teardown) { #f, f, setup, teardown, NULL }
#define cmocka_unit_test_prestate(f, state) { #f, f, NULL, NULL, state }
#define cmocka_unit_test_prestate_setup_teardown(f, setup, teardown, state) { #f, f, setup, teardown, state }
其中 cmocka_unit_test 宏最为简单,只设置测试用例函数,其他为NULL。cmocka_unit_test_prestate_setup_teardown 宏最丰富,可设置测试用例的所有信息。
CMocka 即支持cmocka_run_group_tests
函数执行所有测试用例集,也支持run_test
函数执行单个测试用例。
static void null_test_success(void **state) {
(void) state;
}
int main(void) {
return run_test(null_test_success);
}
不管是哪种方式执行测试,当执行测试用例遇到错误时,会立刻中断并退出当前测试用例,测试程序将继续执行下一测试用例。
官网API手册 The CMocka API 中有这么一张图,它清晰的描述了 CMocka API 由哪些子模块构成。
CMocka 提供了一组用于测试逻辑条件的断言,其使用方法和标准C中的 assert 差不多。比如要测试 add 函数(两个整形数之和),可以:
static void test_add(void **state) {
(void) state;
assert_int_equal(add(1, 2), 3);
}
官网API手册中的 Assert Macros 模块中列出了 CMocka 框架支持的所有断言
CMocka 框架带有模拟函数(Mock Functions)的功能,可以为模拟函数设置期望返回值,设置期望输出参数,检查输入参数,检查调用顺序。
有一些特殊的函数,用于在不执行逻辑测试的情况下,向框架注册通过或失败。这对于测试控制流或其他不需要逻辑测试的情况都非常有用:
立刻中断当前测试用例的执行,将其标记为失败,并继续执行下一个测试用例会(如果有的话)。
跟fail()一样,只是多了打印日志信息。
立刻中断当前测试用例的执行,将其标记为跳过,并继续执行下一个测试用例会(如果有的话)。
源码 cmocka/tests/test_skip.c
演示了 skip() 的用法,可自行查阅,fail() 用法类似。