繁体   English   中英

C 中的函数模拟(用于测试)?

[英]Function mocking (for testing) in 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将使用的定义squarefoo_test.c ,而不是一个在foo.c ,连接可执行文件,只有当foo_test 也就是说,我想将foo.c编译成一个库(我们称之为libfoo.so ),然后用libfoo.so和一些魔法编译foo_test.c ,这样我就会得到一个可执行文件foo_test ,它使用了不同的实现square

听到square未声明为static解决方案会很有帮助,但解决上述情况会更好。

编辑:这似乎没有希望,但这里有一个想法:假设我用-O0 -g编译,所以square不太可能被内联,我应该有符号显示调用在哪里解决。 有没有办法潜入目标文件并换出已解析的引用?

我写了Mimick ,一个用于解决这个问题的 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 的工作原理是对正在运行的可执行文件进行一些内省,并在运行时毒化全局偏移量表,以将函数重定向到我们选择的存根。

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

弱属性导致声明作为弱符号而不是全局符号发出。 这主要用于定义可以在用户代码中覆盖的库函数,尽管它也可以与非函数声明一起使用。 弱符号支持 ELF 目标,也支持使用 GNU 汇编器和链接器时的 a.out 目标。

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

不,没有解决方案。 如果作用域中存在名称与源文件中的函数调用匹配的函数,则将使用该函数。 没有任何声明技巧会让编译器摆脱它。 当链接器处于活动状态时,名称引用将已被解析。

鉴于square()可以全局声明,您可以使用Nala测试框架来替换square()的实现。

所有源代码,包括一个 makefile,都可以在这里找到

头文件foo.h

int square(int x);
int myfunction(int x, int y);

源文件foo.c

#include "foo.h"

int square(int x) { return x * x; }

int myfunction(int x, int y) {
    return square(x) + square(y);
}

测试文件test_foo.c ,以两种替代方式实现测试; 替换实现(按要求)和期望回报。

#include "foo.h"
#include "nala.h"
#include "nala_mocks.h"

static int my_square(int x) { return x + 1; }

/* Replace the implemenation of square(). */
TEST(implementation)
{
    square_mock_implementation(my_square);

    ASSERT_EQ(myfunction(0, 0), 2);
}

/* Expect square(x=0) and return 1. */
TEST(mock)
{
    square_mock(0, 1);

    ASSERT_EQ(myfunction(0, 0), 2);
}

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM