需求分析
在做C语言的单元测试的时候,我们希望在不改动源码的情况下,额外加入单元测试用的代码。借用cmocka这样的测试框架,希望能够通过mock来替代源代码中的函数。例如待测函数是A,而A调用了B,那么最好是可以通过mock_B来让A直接获取到返回值,从而把A定义的流程走完。
尝试
通过搜索,找到了一个可以执行的例子如下
/* test_mocking.c */
#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <cmocka.h>
int get_value();
int __wrap_get_value() {
int v;
v = mock_type(int);
return v;
}
int add_one() {
int v;
v = get_value();
return v + 1;
}
static void add_test(void **state) {
(void)state;
int a;
will_return(__wrap_get_value, 3);
a = add_one();
assert_int_equal(a, 4);
}
int main(int argc, char *argv[]) {
const struct CMUnitTest tests[] = {
cmocka_unit_test(add_test),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}
进行编译,确实可以起到用add_one调用的是 __wrap_get_value而不是get_value的效果。
$ gcc test_mocking.c -I. -Wl,--wrap=get_value -lcmocka && ./a.out
[==========] Running 1 test(s).
[ RUN ] add_test
[ OK ] add_test
[==========] 1 test(s) run.
[ PASSED ] 1 test(s).
但是,注意到源码中只是声明get_value,而并没有实现get_value这个函数。如果实现这个函数再尝试编译执行
/* test_mocking.c */
#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <cmocka.h>
int get_value() {
return 20;
}
......
$ gcc test_mocking.c -I. -Wl,--wrap=get_value -lcmocka && ./a.out
[==========] Running 1 test(s).
[ RUN ] add_test
[ ERROR ] --- 0x15 != 0x4
[ LINE ] --- test_mocking.c:30: error: Failure!
[ FAILED ] add_test
[==========] 1 test(s) run.
[ PASSED ] 0 test(s).
[ FAILED ] 1 test(s), listed below:
[ FAILED ] add_test
1 FAILED TEST(S)
发现wrap就无效了,add_one调用的就是get_value自身定义的实现,而不是__wrap_get_value了!
结论
在stackoverflow找到了一个跟我诉求一样的帖子,回答的人特意提到,要被wrap的函数,必须是未定义实现的
--wrap=symbol
Use a wrapper function for symbol. Any undefined reference to symbol will be resolved to __wrap_symbol. Any undefined reference to __real_symbol will be resolved to symbol.
这样看来,想完全“不打扰”源码中的实现,额外加单元测试的念头,用cmocka是不行了。