2009-02-27 157 views
33

作为将单元测试一些C代码,我们碰上了一些静态函数不能在测试文件被称为问题,无需修改源代码。有没有简单或合理的方法来解决这个问题?如何测试一个静态函数

+0

可能重复[如何测试具有私有方法,字段或内部类的类?](https://stackoverflow.com/questions/34571/how-do-i-test-a-class-that -has-private-methods-fields-or-inner-classes) – Raedwald 2017-12-14 12:48:06

回答

42

我有一个测试用具。在严峻的情况下 - 就像试图测试静电功能,我使用:

#include "code_under_test.c" 
...test framework... 

也就是说,我包括整个包含在测试工具测试功能的文件。这是最后的手段 - 但它有效。

+0

我认为这应该没有任何影响。 – 2009-03-05 04:39:59

+0

我不明白这一点。有人可以请解释。谢谢! – 2014-11-12 06:35:24

+1

@BhupeshPant:你不明白什么?如果一个函数是`static`,它就不能在定义它的转换单元之外(粗略地说,源文件)访问。答案中的解决方案确保将要测试的函数包含在测试代码中,方法是将要测试的源代码复制到通过`#include“code_under_test.c”`指令进行测试的文件中。编译器将要测试的代码以及将测试全部作为单个翻译单元的代码看到,因此测试代码可以调用静态函数,否则这是不可能的。 – 2014-11-12 06:38:20

2

你可以添加一个非静态函数调用静态函数,然后调用非静态函数。

static int foo() 
{ 
    return 3; 
} 

#ifdef UNIT_TEST 
int test_foo() 
{ 
    if (foo() == 3) 
    return 0; 

    return 1; 
} 
#endif 
+0

修改源代码,不是吗? – 2009-02-27 03:38:46

+0

是的,它修改了源,但并没有从foo中移除“静态”。否则,答案几乎是你不能从外部调用静态的。我自由了。 – 2009-02-27 03:44:12

5

没有 - 你不能直接测试的静态函数无需修改源至少有一点(即在C中的静态定义 - 它不能从一个函数在不同的文件调用)。

您可以创建测试文件,仅仅调用静态函数中的一个单独的功能?

例如:

//Your fn to test 
static int foo(int bar) 
{ 
    int retVal; 
    //do something 
    return retVal; 
} 

//Wrapper fn 
int test_foo(int bar) 
{ 
    return foo(bar); 
} 

我们通常不直接考验我们的静态功能,而是确保他们执行的逻辑是充分的被调用函数的不同的测试进行测试。

11

你可以提供更多的信息,为什么你不能调用该函数?

它不可用,因为它对于.c文件是私人的吗?如果是这样,最好的办法是使用条件编译来访问函数,以便其他编译单元可以访问它。例如

SomeHeaderSomewher.h

#if UNIT_TEST 
#define unit_static 
#else 
#define unit_static static 
#endif 

foo.h中

#if UNIT_TEST 
void some_method 
#endif 

Foo.cpp中

unit_static void some_method() ... 
6

用于单元测试,我们实际上有在源文件中的测试码本身,我们在测试时有条件地编译它。这使单元测试可以完全访问所有函数和文件级变量(静态或其他)。

单元测试本身不是一成不变的 - 这使我们能够从单元测试所有编译单元一个超级测试程序调用的单元测试。

当我们的船的代码,我们有条件地编出了单元测试,但是这实际上不是必要的(如果你想确定你是运送正是你测试相同的代码)。

我们一直认为它无价有单元测试在同一个地方,你正在测试,因为它使得它更明显,你需要更新测试是否以及何时改动代码的代码。

2

静态函数实质上是公共(即暴露)函数的辅助函数。所以IMO,你的单元测试应该使用静态函数中所有路径的输入调用公共接口。

公共函数的输出(返回值/副作用)应该用于测试静态效果。

这意味着你需要有适当的存根来捕捉这些副作用。 (例如,如果函数调用文件IO,则需要提供存根以覆盖这些文件IO lib函数)。通过使每个测试套件都是独立的项目/可执行文件并避免链接到任何外部lib函数来实现此目的的最佳方法。你甚至可以嘲笑C函数,但这不值得。

无论如何,这是我迄今使用的方法,它适用于我。祝你好运

2

如果你是Unix环境下您可以在测试文件附加头yourheader_static.h与您的静态函数的声明和翻译OBJ文件code_under_test.o通过objdump --globalize-symbols=syms_name_file全球化局部符号。它们将可见,就好像它们是非静态函数一样。

0

有2种方法可以做到这一点。

  1. 包括C源文件到单元测试源文件,所以静态方法现在在单元测试源文件和可调用的范围。

  2. 做一招:

#define static

在单元测试源文件的头部

它会将关键字static转换为“无”,或者我可以说,它会从您的c源代码中删除static

在一些单元测试工具,我们可以使用配置选项“预处理器”做这一招,只是把在配置:

static=

刀具预订购转换所有static关键字“没什么“

但我必须说,使用这些方法使得static方法(或变量)失去了它的特定含义。

1
#define static 

这是一个非常糟糕的主意。如果你有一个声明为函数本地的变量,它会改变函数的行为。例如:

static int func(int data) 
{ 
    static int count = 0; 

    count += data; 
    return count; 
} 

你可以从单元测试中调用函数,因为func()会被导出,但是代码的基本功能会被修改。

--kurt

1

如果您使用Ceedling并试图使用的#include“code_under_test.c”的方法,该测试版本将失败,因为它会自动尝试建立“code_under_test.c”当一次#included,也因为它是测试的目标。

我已经能够通过稍微修改code_under_test.c代码和其他一些更改来解决它。与此检查整个包住code_under_test.c文件:

#if defined(BUILD) 
... 
#endif // defined(BUILD) 

添加到您的测试工具:

#define BUILD 
#include "code_under_test.c" 

添加BUILD定义到你的Makefile或项目的配置文件:

# Makefile example 
.. 
CFLAGS += -DBUILD 
.. 

现在,您的文件将从您的环境中构建,并从测试工具中包含。 Ceedling现在不能再次构建文件(确保您的project.yml文件不定义BUILD)。

1

以上所有建议的答案(除少数)都建议进行条件编译,这需要修改源文件。因此,这不应该是一个问题,它只会增加混乱(仅用于测试)。相反,你可以做这样的事情。

说你的功能进行测试的

static int foo(int); 

你让叫testing_headers.h另一头文件,这将有内容 -

static in foo(int); 
int foo_wrapper(int a) { 
    return foo(a); 
} 

现在在编译的C文件进行测试即可从编译器选项强制包含此头文件。

对于clang和gcc,标志是--include。对于Microsoft C编译器,它是/FI

这将需要对您的c文件进行绝对0的更改,您将能够为您的函数编写一个非静态包装。

如果你不想要一个非静态的包装器,你也可以创建一个非静态的全局函数指针初始化为foo。

然后,您可以使用此全局函数指针调用该函数。

0

只需添加到乔纳森·莱弗勒接受的答案,并详细说明其他的提到的包装功能:

  1. 创建一个测试源文件,如test_wrapper_foo.c,其中foo.c的是原。
  2. 在test_wrapper_foo.c:
#include "foo.c" 

// Prototype 
int test_wrapper_foo(); 

// wrapper function 
int test_wrapper_foo() { 
    // static function to test 
    return foo(); 
} 

假设foo.c中静态函数foo原型:int foo的(无效);

透过生成文件的文件foo.c代替

  • 构建test_wrapper_foo.c(注意,这将不会被其它外部函数打破功能有关的任何依赖性foo.c中)

  • 在您的单元测试脚本中,调用test_wrapper_foo()而不是foo()。

  • 这种方法使原始源保持完好,同时允许您从测试框架访问函数。