C中的函数模拟(用于测试)?
我想在C中编写C库的测试。我想模拟测试的一些函数。
假设我的库是从以下源编译的:
/* foo.h */ int myfunction(int x, int y); /* foo.c */ #include "foo.h" static int square(int x) { return x * x; } int myfunction(int x, int y) { return square(x) + square(y); }
我想写一个这样的测试:
/* foo_test.c */ #include "foo.h" static int square(int x) { return x + 1; } int main(void) { assert(myfunction(0, 0) == 2); return 0; }
有没有什么方法可以编译,以便myfunction
将使用myfunction
中square
的定义,而不是foo.c
中的foo_test.c
,只有在链接可执行文件foo_test
? 也就是说,我想将foo.c
编译成一个库(让我们称之为libfoo.so
),然后使用libfoo.so
和一些魔法编译foo_test.c
以便我得到一个可执行文件foo_test
,它使用不同的实现square
。
听取square
未声明为static
解决方案会很有帮助,但解决上述情况会更好。
编辑:似乎没有希望,但这里有一个想法:假设我使用-O0 -g
编译,因此square
不太可能被内联,我应该有符号显示调用的解析位置。 有没有办法潜入目标文件并换出已解决的参考?
看起来您正在使用GCC,因此您可以使用weak属性:
weak属性导致声明作为弱符号而不是全局符号发出。 这主要用于定义可以在用户代码中重写的库函数,尽管它也可以与非函数声明一起使用。 ELF目标支持弱符号,并且在使用GNU汇编器和链接器时也支持a.out目标。
不,没有解决方案。 如果范围内的函数的名称与源文件中的函数调用匹配,则将使用该函数。 没有声明技巧会将编译器与它进行对话。 当链接器处于活动状态时,名称引用将已经解析。
我写了一个Mimick ,一个用于解决这个问题的C函数的模拟 /存根库。
假设square不是静态的也不是内联的(因为否则它会绑定到编译单元和使用它的函数)并且你的函数被编译在一个名为“libfoo.so”的共享库中(或者你平台的命名约定是什么) ),这就是你要做的:
#include #include #include /* Define the blueprint of a mock identified by `square_mock` that returns an `int` and takes a `int` parameter. */ mmk_mock_define (square_mock, int, int); static int add_one(int x) { return x + 1; } int main(void) { /* Mock the square function in the foo library using the `square_mock` blueprint. */ mmk_mock("square@lib:foo", square_mock); /* Tell the mock to return x + 1 whatever the given parameter is. */ mmk_when(square(mmk_any(int)), .then_call = (mmk_fn) add_one); /* Alternatively, tell the mock to return 1 if called with 0. */ mmk_when(square(0), .then_return = &(int) { 1 }); assert(myfunction(0, 0) == 2); mmk_reset(square); }
这是一个完整的模拟解决方案,如果你只想要存根(并且不关心测试交互),你可以做类似的事情:
#include #include #include static int my_square(int x) { return x + 1; } int main(void) { mmk_stub("square@lib:foo", my_square); assert(myfunction(0, 0) == 2); mmk_reset(square); }
Mimick通过对正在运行的可执行文件使用一些内省并在运行时中毒全局偏移表来将函数重定向到我们选择的存根。
您正在寻找的内容在本文中描述: 使用C语言中的模拟对象进行unit testing
在像你这样的情况下,我使用Typemock Isolator ++ API。
它允许您通过自己的实现替换方法的原始行为。 但是,由于语言的具体细节,您应该避免测试下的函数和相同的名称。
#include "foo.h" static int square_test(int x) { return x + 1; } TEST_CLASS(ArgumentTests) { public: TEST_METHOD_CLEANUP(TearDown) { ISOLATOR_CLEANUP(); } TEST_METHOD(TestStaticReplacedStatic) { PRIVATE_WHEN_CALLED(NULL, square).DoStaticOrGlobalInstead(square_test, NULL); Assert::IsTrue(myfunction(0, 0) == 2); } };
希望它对你有用,祝你好运!