是否可以在运行时交换C函数实现?

时间:2013-02-22 09:46:21

标签: c unit-testing mocking facade

我已经实现了一个在下面使用C函数的Facade模式,我想正确测试它。

我实际上无法控制这些C函数。它们在标题中实现。现在我#ifdef在测试中使用生产中的真实头文件和我的模拟头文件。 C中有没有办法通过覆盖C函数地址或其他东西在运行时交换C函数?我想摆脱代码中的#ifdef。

4 个答案:

答案 0 :(得分:1)

为了扩展Bart的答案,请考虑以下简单的例子。

#include <stdio.h>
#include <stdlib.h>

int (*functionPtr)(const char *format, ...);

int myPrintf(const char *fmt, ...)
{
    char *tmpFmt = strdup(fmt);
    int i;
    for (i=0; i<strlen(tmpFmt); i++)
        tmpFmt[i] = toupper(tmpFmt[i]);

// notice - we only print an upper case version of the format
// we totally disregard all but the first parameter to the function
    printf(tmpFmt);

    free(tmpFmt);
}

int main()
{

    functionPtr = printf;
    functionPtr("Hello world! - %d\n", 2013);

    functionPtr = myPrintf;
    functionPtr("Hello world! - %d\n", 2013);

    return 0;
}

<强>输出

Hello World! - 2013
HELLO WORLD! - %D

答案 1 :(得分:1)

奇怪的是,你甚至需要一个ifdef选择的标题。代码测试和你的模拟应该具有完全相同的函数签名,以便成为模块到测试的正确模拟。然后,生产编译和测试编译之间唯一的变化就是您为链接器提供了哪些 .o 文件。

答案 2 :(得分:1)

使用Typemock Isolator ++可以不创建不必要的新间接间接。它可以在测试中完成,而无需更改生产代码。请考虑以下示例:

你的代码中有Sum函数:

int Sum(int a, int b)
{
    return a+b;
}

您希望将其替换为Sigma进行测试:

int Sigma(int a, int b)
{
    int sum = 0;

    for( ; 0<a ; a--)
    {
        sum += b;
    }

    return sum;
}

在你的测试中,在使用之前模拟Sum:

WHEN_CALLED :调用您想要伪造的方法。

ANY_VAL :指定要应用模拟的args值。在这种情况下任何2个整数。

* DoStaticOrGlobalInstead :您想要Sum的替代行为。 在这个例子中,我们称之为Sigma。

TEST_CLASS(C_Function_Tests)
{
public:

TEST_METHOD(Exchange_a_C_function_implementation_at_run_time_is_Possible)
{
    void* context = NULL; //since Sum global it has no context
    WHEN_CALLED(Sum (ANY_VAL(int), ANY_VAL(int))).DoStaticOrGlobalInstead(Sigma, context);

    Assert::AreEqual(2, Sum(1,2));
}
};

* DoStaticOrGlobalInstead 的 可以设置其他类型的行为,而不是调用替代方法。您可以抛出异常,返回值,忽略方法等...

例如:

TEST_METHOD(Alter_C_Function_Return_Value)
{
    WHEN_CALLED(Sum (ANY_VAL(int), ANY_VAL(int))).Return(10);
    Assert::AreEqual(10, Sum(1,2));
}

答案 3 :(得分:0)

我不认为在运行时覆盖函数是个好主意。首先,可执行段可以设置为只读,即使不是,如果你的程序集太大,你最终还是会踩到另一个函数的代码。

我认为您应该为要使用的一个和另一组实现创建类似函数指针集合的东西。每次要调用函数时,都将从所选函数指针集合中调用。完成后,您还可以使用代理函数(只需从所选集调用)来隐藏函数指针语法。