【发布时间】:2021-03-29 11:40:13
【问题描述】:
我正在为依赖于第 3 方 C 和 C++ 库以及第 1 方 C 库的 C++ 类编写单元测试。我遇到了麻烦,因为我希望被测类使用模拟的第 3 方 C 库,而测试运行程序使用真正的第 3 方 C 库。
- 我正在编写单元测试,而被测类依赖于
libx。 - 我创建了一个
libmockx,它允许我测试参数并注入返回值。 - 被测类需要链接到
libmockx,以便我可以检查和控制它的行为。 - 单元测试应用程序,需要链接到
libx才能制定/解析libx数据类型。
使用哪种模式或方法将测试运行器链接到libx,将被测类链接到libmockx,然后将测试运行器链接到被测类?许多解决方案都在讨论让链接器做“脏活”,但ld 有 100 个参数,我不知道如何让它工作。
目前,我对所有模拟实现都有重新定义错误,我需要一种解决方法(无论是否涉及链接器)。
在下面编辑: (回应 cmets)
想象 7 个文件:
-
test.cpp- 测试运行者 -
object.cpp- 正在测试的object -
object.hpp-object标头 -
mock-parameters.h- 提供对测试的模拟参数访问 -
mock-x.c-x的模拟实现 -
x.c-x实现 -
x.h-x标头
在我需要实例化和操作x 对象之前,我能够通过对g++ 的一次调用来编译我的测试:
g++ test.cpp object.cpp mock_x.c
我正在尝试向test.cpp 添加一个测试,它将提供和测试x 对象结果值。现在,我需要将test.cpp 与x.c 链接,同时仍将object.cpp 与mock_x.c 链接。
当我将x.c 添加到编译列表时,我收到以下(预期的)错误:
/usr/bin/ld: /tmp/ccDVQxtN.o: in function `set_foo(X*, int)':
mock-x.c:(.text+0x0): multiple definition of `set_foo(X*, int)'; /tmp/cc3t8CjP.o:x.c:(.text+0x14): first defined here
collect2: error: ld returned 1 exit status
x.h
#ifndef X_H
#define X_H
typedef struct X {
int foo;
char bar;
} X;
X * create_x (void);
int set_foo (X *, int);
char set_bar (X *, char);
void delete_x (X *);
#endif // X_H
x.c
#include "x.h"
#include "stdlib.h"
X * create_x (void) {
return (X *)malloc(sizeof(X));
}
int set_foo (X * x, int i) {
x->foo = i;
return i;
}
char set_bar (X * x, char c) {
x->bar = c;
return c;
}
void delete_x (X * x) {
free(x);
}
mock-x.c
#include "x.h"
#include "mock-parameters.h"
Set_foo_params set_foo_params;
int set_foo (X * x, int i) {
// Stash parameter(s)
set_foo_params.x = x;
set_foo_params.i = i;
return set_foo_params.result;
}
object.hpp
#ifndef OBJECT_HPP
#define OBJECT_HPP
#include "x.h"
class Object {
public:
void embed_x(X *);
int increment_foo(int);
private:
X * _x;
};
#endif // OBJECT_HPP
object.cpp
#include "object.hpp"
void Object::embed_x (X * x) {
_x = x;
}
int Object::increment_foo (int i) {
++i;
return set_foo(_x, i);
}
mock-parameters.h
#include "x.h"
typedef struct Set_foo_params {
X * x;
int i;
int result;
} Set_foo_params;
extern Set_foo_params set_foo_params;
test.cpp
#include "object.hpp"
#include "mock-parameters.h"
int test_object_update_foo_correctly_invokes_x_set_foo (void) {
int result;
Object object;
// Setup
X * x = create_x();
object.embed_x(x);
// Execute
object.increment_foo(7);
// Test
if (8 == set_foo_params.i) {
result = 0;
} else {
result = 1;
}
delete_x(x);
return result;
}
int test_object_update_foo_correctly_returns_x_set_foo_result (void) {
int result;
Object object;
// Setup
X * x = create_x();
object.embed_x(x);
set_foo_params.result = 5;
// Execute
int output = object.increment_foo(0);
// Test
if (5 == output) {
result = 0;
} else {
result = 2;
}
delete_x(x);
return result;
}
int main (void) {
int result;
result |= test_object_update_foo_correctly_invokes_x_set_foo();
result |= test_object_update_foo_correctly_returns_x_set_foo_result();
return result;
}
【问题讨论】:
-
建议将您正在使用的构建工具添加到问题中。它将直接影响所提供的解决方案。
-
我现在正在手工编译。想象一下 6 个文件,
test.cpp、object.cpp、mock_x.c、x.c、object.hpp、x.h。 -
在这种情况下,您只需要在命令行上指定不同的库。也许在问题中添加您当前正在做什么的示例会动摇松散的答案。
-
只是一个想法:使用
--wrap选项拦截对库的所有调用怎么样?您可以使用外部标志来决定在包装函数中模拟函数或调用真实函数。 -- 请提供minimal reproducible example 好吗? -
object.c 中使用的
API(具有不同的 x.c 和 mock-x.c 实现)的功能和 test.cpp 中使用的功能是否重叠?
标签: c++ c unit-testing mocking linker