5

从这篇文章Unit testing with mock objects in C

这是通过使用--wrap链接器选项来完成的,该选项将包装函数的名称作为参数。如果测试是使用 gcc 编译的,则调用可能如下所示:

$ gcc -g -Wl,--wrap=chef_cook waiter_test.c chef.c

在 Visual Studio 中编译 C 项目时如何执行此操作?

4

1 回答 1

3

可以通过MSVC Linker 中的选项来模拟--wrapin 。ld/ALTERNATENAME

我们从两个编译单元开始,比如foo.o从 编译foo.c,其外部函数在foo.hmain.o中声明main.c。(如果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("%s\n", x ? "wrapped" : "original");
}

的返回值为int foo()0,因此上面的代码片段将输出“原始”。

现在我们用别名覆盖实际的实现:#include "foo.h"inmain.c被替换为

#define foo real_foo
#include "foo.h"
#undef foo
#pragma comment(linker, "/alternatename:real_foo=foo")

让我解释一下这里发生了什么:

  1. 通过#define foo real_foo, 中的函数声明foo.h被修改为int real_foo().
  2. 但是,符号 infoo.o仍然以 命名int foo(),而不是别名int real_foo()。这就是我们需要/alternatename链接器开关的原因。
  3. "/alternatename:real_foo=foo"告诉链接器,如果找不到名为 的符号,请在抛出错误之前重real_foo试。foo
  4. 显然没有定义int real_foo()。MSVC 链接器将int foo()在每次出现int real_foo().

由于之前的实现已经被别名,现在我们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("%s\n", x ? "wrapped" : "original");
}

内置在 MSVC 中,它将输出“包装”。

于 2021-02-26T02:20:57.867 回答