【问题标题】:Is it possible to exchange a C function implementation at run time?是否可以在运行时交换 C 函数实现?
【发布时间】:2013-02-07 21:26:25
【问题描述】:

我已经实现了一个在底层使用 C 函数的外观模式,我想对其进行正确测试。

我并不能真正控制这些 C 函数。它们在标头中实现。现在我#ifdef 在生产中使用真正的头文件,在测试中使用我的模拟头文件。 C中有没有办法通过覆盖C函数地址或其他方式在运行时交换C函数?我想去掉我代码中的#ifdef。

【问题讨论】:

  • 看看微软的弯路。你可能会钩住他们
  • 查看函数指针。
  • #ifdef 有什么问题?
  • @RomanSaveljev #ifdef 适用于编译时,而不是运行时。
  • 如果您要模拟测试工具中的函数,那么在测试 C 代码时这是一个非常好的方法。没有必要让它变得更复杂。最好您只希望在测试代码中使用#ifdefs,而不是在真正的标头/生产代码中。

标签: c unit-testing mocking facade


【解决方案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

【讨论】:

    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

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

      我认为您应该为要使用的一组实现和另一组实现创建类似函数指针集合的东西。每次你想调用一个函数时,你都会从选定的函数指针集合中调用。完成此操作后,您还可以使用代理函数(简单地从选定集合中调用)来隐藏函数指针语法。

      【讨论】:

        【解决方案4】:

        可以使用 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 之前模拟 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));
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2014-07-09
          • 1970-01-01
          • 2012-06-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-11-29
          相关资源
          最近更新 更多