如何testing一个静态函数

当对某些C代码应用unit testing时,我们遇到了一个问题,即一些静态函数不能在testing文件中调用,而不修改源代码。 有没有简单或合理的方法来解决这个问题?

我有一个testing用具。 在可怕的情况下 – 比如试图testing一个静态函数,我使用:

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

也就是说,我在testing工具中包含了包含被测函数的整个文件。 这是最后的手段 – 但它的工作。

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

它是不可用的,因为它是一个.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() ... 

对于unit testing,我们实际上在源文件本身有testing代码,并且在testing时有条件地编译它。 这使unit testing可以完全访问所有函数和文件级variables(静态或其他)。

unit testing本身并不是静态的 – 这允许我们从单个testing所有编译单元的单个超级testing程序调用unit testing。

当我们发布代码时,我们有条件地编译出unit testing,但是这实际上并不是必须的(如果你想确定你运送的是完全相同的代码)。

我们总是发现将unit testing放在你正在testing的代码的相同位置是非常有价值的,因为更明显的是你需要在代码改变的时候更新testing。

不行 – 你不能直接testing一个静态函数而不修改源代码至less有一点(也就是C语言中静态的定义 – 它不能从一个不同的文件中的函数中调用)。

你可以在testing文件中创build一个独立的函数来调用静态函数吗?

例如:

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

我们通常不直接testing我们的静态函数,而是确保它们执行的逻辑被调用函数的不同testing充分地testing。

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

 static int foo () { return 3; } #ifdef UNIT_TEST int test_foo () { if (foo () == 3) return 0; return 1; } #endif 

静态函数实质上是对公众(即暴露)函数的辅助函数。 所以海事组织,你的unit testing应该调用公共接口的input,执行静态函数中的所有path。

应该使用公共函数的输出(返回值/副作用)来testing静态的效果。

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

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

如果你在Unix环境下,你可以在testing文件中包含额外的头文件yourheader_static.h ,声明你的静态函数,并通过objdump --globalize-symbols=syms_name_file翻译obj文件code_under_test.oobjdump --globalize-symbols=syms_name_file化本地符号。 它们将可见,就像它们是非静态函数一样。

 #define static 

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

 static int func(int data) { static int count = 0; count += data; return count; } 

你可以从unit testing中调用函数,因为func()会被导出,但是代码的基本function会被修改。

–kurt

如果您使用Ceedling并尝试使用#include“code_under_test.c”方法,那么testing版本将会失败,因为在#included中会自动尝试构build“code_under_test.c”一次,也是因为它是testing的目标。

我已经能够通过稍微修改code_under_test.c代码和其他一些更改来解决这个问题。 用这个检查包装整个code_under_test.c文件:

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

将其添加到您的testing工具中:

 #define BUILD #include "code_under_test.c" 

BUILD定义添加到您的Makefile或项目configuration文件中:

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

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

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

说你要testing的function是

 static int foo(int); 

你做另一个名为testing_headers.h的头文件将有内容 –

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

现在在编译你的c文件进行testing的时候,你可以强制从编译器选项中包含这个头文件。

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

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

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

然后可以使用这个全局函数指针调用函数。

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

  1. 将c源文件包含到unit testing源文件中,所以静态方法现在处于unit testing源文件的范围之内并可调用。

  2. 做个窍门:

#define static

在unit testing源文件的头部。

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

在一些unit testing工具中,我们可以使用configuration选项“预处理器”来做这个技巧,只要在configuration中:

static=

该工具将所有static关键字转换为“无”

但是我必须说,用这些方法使static方法(或variables)失去了它的具体含义。