C++单元测试链接难题:被测类连Mock库,测试程序连真实libx
解决C++单元测试中真实库与Mock库的符号冲突问题
这个符号重定义问题在C/C++单元测试里太常见了——你需要让被测类用Mock版的libx来控制行为,同时测试代码用真实版的libx来创建/销毁对象,本质是要让同一个符号(比如set_foo)的两个实现在同一个二进制里和平共存,而链接器默认不允许重复定义。下面给你几个实用的解决方案,按推荐程度排序:
方案一:使用GCC链接器的--wrap选项(最推荐,修改最少)
GNU链接器有个--wrap特性,可以把对目标符号的调用重定向到另一个符号,完美解决这种“部分代码用Mock,部分用真实实现”的场景。
步骤:
- 修改Mock实现的函数名:把
mock-x.c里的set_foo改成__wrap_set_foo:
#include "x.h" #include "mock-parameters.h" Set_foo_params set_foo_params; // 把函数名改成__wrap_set_foo int __wrap_set_foo (X * x, int i) { set_foo_params.x = x; set_foo_params.i = i; return set_foo_params.result; }
- 编译并链接时添加
--wrap选项:
用以下命令编译所有文件,链接器会自动把被测类(object.cpp)里对set_foo的调用替换成__wrap_set_foo(Mock实现),而测试代码(test.cpp)里对create_x/delete_x的调用会使用真实的x.c实现:
# 编译各个源文件 gcc -c x.c -o x.o gcc -c mock-x.c -o mock-x.o g++ -c object.cpp -o object.o g++ -c test.cpp -o test.o # 链接时添加--wrap选项,指定要包装的符号 g++ test.o object.o mock-x.o x.o -Wl,--wrap=set_foo
运行生成的可执行文件,就能正常执行测试了——被测类调用Mock的set_foo做参数校验和返回值注入,测试代码用真实的libx创建/销毁对象,完全隔离。
方案二:符号隐藏+静态库打包(跨平台友好)
如果需要跨平台支持(比如Windows,GCC的visibility属性也能在MinGW用),可以通过符号隐藏把被测类和Mock库打包成一个独立模块,让Mock符号只在模块内部可见,不会和真实库的符号冲突。
步骤:
- 修改Mock参数头文件:让测试代码能访问Mock的参数结构体,给
set_foo_params添加可见性标记:
#include "x.h" typedef struct Set_foo_params { X * x; int i; int result; } Set_foo_params; // 添加visibility属性,让这个全局变量能被测试代码访问 extern __attribute__((visibility("default"))) Set_foo_params set_foo_params;
- 编译时启用符号隐藏:
# 编译Mock和被测类时,用-fvisibility=hidden隐藏所有内部符号 gcc -c mock-x.c -o mock-x.o -fvisibility=hidden g++ -c object.cpp -o object.o -fvisibility=hidden # 把这两个目标文件打包成静态库 ar rcs libobjectmock.a object.o mock-x.o # 编译测试代码,链接真实的x.c和静态库 g++ test.cpp x.c -L. -lobjectmock
这样,libobjectmock.a里的set_foo是内部隐藏符号,外部看不到,测试代码里的create_x/delete_x来自真实的x.c,被测类内部调用的是Mock的set_foo,完全不会冲突。
方案三:动态链接+LD_PRELOAD(Linux专属,零代码修改)
如果你在Linux环境下,可以利用动态链接的LD_PRELOAD机制,让被测类优先加载Mock库,测试代码使用真实库。
步骤:
- 编译真实库和Mock库为共享库:
# 编译真实libx为共享库 gcc -shared -fPIC x.c -o libx.so # 编译Mock libx为共享库 gcc -shared -fPIC mock-x.c -o libmockx.so
- 编译被测类和测试代码:
# 编译被测类,链接Mock库 g++ -c object.cpp -o object.o -L. -lmockx # 编译测试代码,链接真实库 g++ test.cpp object.o -L. -lx
- 运行测试时用LD_PRELOAD加载Mock库:
LD_PRELOAD=./libmockx.so ./a.out
这个方法不需要修改任何代码,利用Linux动态链接器的优先级特性,让被测类调用的set_foo来自Mock库,测试代码调用的create_x/delete_x来自真实库。
内容的提问来源于stack exchange,提问作者Zak
相关产品推荐
相关产品推荐

