2

如何在 Ceedling 中为使用静态全局变量的函数编写测试?我想测试变量的每个可能值以实现良好的测试覆盖率。

//Pseudo code for file_under_test.c
static int global_var;

int func_under_test(){ 

   switch(global_var){
    case x:
      return some_value;
    case y:
      return some_other_value;
    .
    .
    .
    .
    default:
      return something; 
   }

}
4

4 回答 4

1

这是单元测试 C 代码中的一个超级常见问题,我所知道的最常见的解决方案是在测试时将 static 关键字定义为不存在。这需要一些计划并且在遗留代码中很难做到,但是我计划测试的任何静态都被其他一些字符串替换。通常是 STATIC 或更好的 TESTABLE_STATIC。

请记住,大多数单元测试框架都设置了编译时宏 TEST,因此您的代码将是

//Pseudo code for file_under_test.c
#ifdef TEST
#define TESTABLE_STATIC 
#else
#define TESTABLE_STATIC static
#endif


TESTABLE_STATIC int global_var;

int func_under_test(){ 

   switch(global_var){
    case x:
      return some_value;
    case y:
      return some_other_value;
    .
    .
    .
    .
    default:
      return something; 
   }

}

然后在您的测试文件中,您只需将变量视为全局变量

// your ceedling test 
#include <your_functions.h>

extern int global_var;
void test_function_under_test(void)
{
    // your test code setting global_var as needed
    global_var = some_test_value;
    TEST_ASSERT_EQUAL(expected_val, func_under_test());
}

我通常将 TESTABLE_STATIC 隐藏在项目头文件中,或者如果您有 datatypes.h 文件,那么它通常在我的项目中随处可用。

这也适用于在翻译单元中对静态函数进行单元测试。

于 2020-05-14T19:34:16.417 回答
0

我使用了一个包装器,它包含原始 C 文件并添加了一些帮助程序进行测试所以你有一个未更改的原始 c 源代码,但可以访问所有需要的内部。

文件 Wrapped_for_test.h

#include <file_under_test.h>
void set_for_test(int value);

文件 Wrapped_for_test.c

#include <file_under_test.c>

void set_for_test(int value)
{
  global_var = value;
}
于 2020-08-20T14:03:38.333 回答
0

这个问题与 Ceedling(或 Unity、CMock 等)并没有什么特别的关系,但我认为这是一个以非常具体的方式解释“单元”一词的例子。我的回答的简短版本是,您在这里编写的示例函数并没有真正构成一个独立的“单元”,所以我认为这不是真正的“单元测试”。

只有当一个“函数”是一个纯函数或者你可以找到合适的接缝(例如,存根、间谍或外部接口的模拟)时,才将“函数”视为一个“单元”!否则,您将不得不检查测试中的实现细节,这使得测试非常脆弱。

为了拥有可测试代码的“单元”,您需要能够看到被测单元的效果(例如比较返回值和/或检查其他副作用)并且能够刺激单元被测(例如,通过将参数传递给函数或首先设置被测单元所依赖的一些副作用)。

示例函数依赖于其他函数的副作用(具有修改静态“全局”变量的副作用),因此在这种情况下,“适当的单元”需要包含触发这些方面的任何函数效果。我想你的文件已经至少有一个这样的函数,或者你的示例代码永远不会返回任何不同的东西*。

*除非您的示例实际上具有修改静态变量本身的副作用。在这种情况下,至少应该有一个重置“全局状态”的函数,否则您的测试将不会相互隔离(即很难使它们与顺序无关)。一个更好的解决方案是通过参数显式公开你的状态的依赖关系func_under_test,喜欢func_under_test(struct some_opaque_type *state)和添加一个struct some_opaque_type *init_for_func_under_test()函数。

TL;DR:如果您的函数不是纯函数(例如,它依赖于隐藏状态和/或本身具有副作用)或者如果您没有适当的“接缝”(例如存根或间谍),那么还包括函数它可以修改隐藏状态或验证您对被测单元定义的副作用。

于 2020-05-10T10:36:24.717 回答
-1

您可以创建一个测试辅助函数,用于在调用之前file_under_test.c设置。如果需要,此测试函数帮助程序可以仅出于测试目的而编译(使用特定的#ifdef),因此如果您的代码是为例如产品构建的,则它不会与其余代码一起提供。global_varfunc_under_test()

file_under_test.h:

void set_for_test(int value);

file_under_test.c

#ifdef TESTS
void set_for_test(int value)
{
  global_var = value;
}
#endif

测试文件.c:

#include <assert.h>
#include "file_under_test.h"

// some other tests
set_for_test(3);
assert (func_under_test() == something);
//...
于 2020-05-09T08:19:37.857 回答