来自本文用C:中的模拟对象进行单元测试
这是通过使用
--wrap
链接器选项来完成的,该选项将封装函数的名称作为参数。如果测试是使用gcc编译的,那么调用可能看起来像:
$ gcc -g -Wl,--wrap=chef_cook waiter_test.c chef.c
在visual studio中编译C项目时,如何做到这一点?
ld
中的--wrap
可以通过MSVC Linker中的/ALTERNATENAME
选项进行模拟。
我们从两个编译单元开始,比如从foo.c
编译的foo.o
,其外部函数在foo.h
中声明,以及从main.c
中声明main.o
。(如果foo
被编译成一个库,情况不会有太大变化。)
// foo.h
int foo();
// foo.c
int foo() {
return 0;
}
// main.c
#include <stdio.h>
#include "foo.h"
int main() {
int x = foo();
printf("%sn", x ? "wrapped" : "original");
}
int foo()
的返回值为0,因此上面的代码片段将输出"0";"原件";。
现在我们用一个别名覆盖实际实现:main.c
中的#include "foo.h"
被取代
#define foo real_foo
#include "foo.h"
#undef foo
#pragma comment(linker, "/alternatename:real_foo=foo")
让我解释一下这里发生了什么:
#define foo real_foo
将foo.h
中的函数声明修改为int real_foo()
- 然而,
foo.o
中的符号仍然以int foo()
命名,而不是以别名int real_foo()
命名。这就是为什么我们需要/alternatename
链接器开关 "/alternatename:real_foo=foo"
告诉链接器,如果找不到名为real_foo
的符号,请在抛出错误之前再次尝试foo
- 显然
int real_foo()
没有定义。MSVC链接器将搜索CCD_ 26,并在每次出现CCD_
由于前面的实现已经被别名化,现在我们通过宏将int foo()
重定向到我们的新实现:
int wrap_foo() {
return real_foo() + 1;
}
#define foo wrap_foo
我们在这里结束了。最后main.cpp
看起来像:
#include <stdio.h>
#define foo real_foo
#include "foo.h"
#undef foo
#pragma comment(linker, "/alternatename:real_foo=foo")
int wrap_foo() {
return real_foo() + 1;
}
#define foo wrap_foo
int main() {
int x = foo();
printf("%sn", x ? "wrapped" : "original");
}
内置在MSVC中;"包裹";。