问题 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 将使用的定义 square 在 foo_test.c而不是那个 foo.c,仅在链接可执行文件时 foo_test?也就是说,我想编译 foo.c 进入图书馆(让我们称之为 libfoo.so),然后编译 foo_test.c 同 libfoo.so 和一些魔法,以便我得到一个可执行文件 foo_test 它使用不同的实现 square

听取何时解决方案会很有帮助 square 没有宣布 static,但解决上述情况会更好。

编辑:似乎没有希望,但这是一个想法:假设我编译 -O0 -g 所以不太可能 square 将被内联,我应该有符号显示呼叫被解决的位置。有没有办法潜入目标文件并换出已解决的参考?


10799
2018-01-22 05:11


起源

LD_PRELOAD 能给你这个,但我希望别人有更好的答案。 - sarnold
不适用于静态功能 - bdonlan
我不认为 LD_PRELOAD 打算这样做。我希望能够在已经编译的库中更改一个函数(可能是静态的)。据我所知(也许这是假的),符号 square 内 myfunction 在我尝试链接之前已经解决了 foo_test。 - leif
当你的函数是静态的,那么答案是否定的,你绝对不能这样做。静态函数可以完全不存在内联和优化。 - n.m.
@ n.m。非静态函数也可以内联和优化。具有链接时优化的现代编译器将会这样做,GCC和Clang / LLVM都是例子。 - Dietrich Epp


答案:


看起来您正在使用GCC,因此您可以使用weak属性:

weak属性导致声明以弱的形式发出   符号而不是全球。 这主要用于定义   可以在用户代码中重写的库函数, 虽然它可以   也可以与非函数声明一起使用。弱符号是   支持ELF目标,以及使用时的a.out目标   GNU汇编器和链接器。

http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html


4
2018-01-22 23:04



谢谢。我可以从这里拿走它,但是你可以给我一些关于修改弱引用的提示吗? - leif
只需定义一个非弱的同名函数,然后它应该覆盖库中的弱函数。 - David Grayson


不,没有解决方案。如果范围内的函数的名称与源文件中的函数调用匹配,则将使用该函数。没有声明技巧会将编译器与它进行对话。当链接器处于活动状态时,名称引用将已经解析。


4
2018-01-22 07:39





我写了 模拟天生,一个用于解决此问题的C函数的模拟/存根库。

假设square不是静态的也不是内联的(因为否则它会绑定到编译单元和使用它的函数)并且你的函数被编译在一个名为“libfoo.so”的共享库中(或者你平台的命名约定是什么) ),这就是你要做的:

#include <stdlib.h>
#include <assert.h>
#include <mimick.h>

/* 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);
}

这是一个完整的模拟解决方案,如果你只想要存根 square (并且不关心测试交互),你可以做类似的事情:

#include <stdlib.h>
#include <assert.h>
#include <mimick.h>

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通过对正在运行的可执行文件使用一些内省并在运行时中毒全局偏移表来将函数重定向到我们选择的存根。


4
2018-04-10 16:03





您正在寻找的内容在本文中描述: 使用C中的模拟对象进行单元测试


2
2017-08-02 07:30





在像你这样的情况下,我使用 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);
    }
};

希望它对你有用,祝你好运!


0
2018-03-03 09:47



问题是关于C,但你的答案是关于C ++。 - Simon Kissane