首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >编写C代码的单元测试

编写C代码的单元测试
EN

Stack Overflow用户
提问于 2013-06-06 18:57:41
回答 3查看 14.1K关注 0票数 20

我是C++开发人员,在测试方面,很容易通过注入依赖项、重写成员函数等方法来测试类,这样就可以轻松地测试边缘案例。但是,在C语言中,您不能使用这些优秀的特性。我发现很难将单元测试添加到代码中,因为C代码的一些“标准”编写方式。解决以下问题的最佳方法是什么:

传递大型“上下文”结构指针的

代码语言:javascript
复制
void some_func( global_context_t *ctx, .... )
{
  /* lots of code, depending on the state of context */
}

不容易测试依赖函数的失败:

代码语言:javascript
复制
void some_func( .... )
{
  if (!get_network_state() && !some_other_func()) {
    do_something_func();
    ....
  }
  ...
}

具有许多参数的函数:

代码语言:javascript
复制
void some_func( global_context_t *, int i, int j, other_struct_t *t, out_param_t **out, ...)
{
  /* hundreds and hundreds of lines of code */
}

静态或隐藏函数:

代码语言:javascript
复制
static void foo( ... )
{
  /* some code */
} 

void some_public_func( ... }
{
  /* call static functions */
  foo( ... );
}
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-06-07 02:03:31

总的来说,我同意韦斯的回答--在不考虑测试的代码中添加测试将更加困难。C的固有特性并不能使测试变得不可能--但是,因为C没有强迫您以特定的风格编写,所以编写很难测试的C代码也非常容易。

在我看来,编写带有测试的代码将鼓励更短的函数,并且很少有争论,这有助于减轻示例中的一些痛苦。

首先,您需要选择一个单元测试框架。这个问题中有很多例子(遗憾的是,很多答案都是C++框架--我建议不要使用C++来测试C)。

我个人使用TestDept,因为它使用简单,轻量级,并允许顽固。然而,我不认为它是非常广泛的使用。如果您正在寻找一个更流行的框架,许多人推荐检查 --如果您使用自动化,这是很棒的。

以下是您的用例的一些具体答案:

传递一个大型“上下文”结构指针

对于这种情况,可以手动设置预条件,然后在函数运行后检查struct的状态。使用简短的函数,每个测试都将相当简单。

不是测试依赖函数失败的简单方法

我认为这是单元测试的最大障碍之一。我已经成功地使用了TestDept,它允许依赖函数的运行时顽固性。这对于分解紧密耦合的代码是很好的。下面是他们文档中的一个例子:

代码语言:javascript
复制
void test_stringify_cannot_malloc_returns_sane_result() {
  replace_function(&malloc, &always_failing_malloc);
  char *h = stringify('h');
  assert_string_equals("cannot_stringify", h);
}

根据您的目标环境,这可能对您有用,也可能不适用。有关更多详细信息,请参阅他们的文件

多参数函数

这可能不是你想要的答案,但我会把它们分解成更小的函数,参数更少。测试起来容易多了。

静态或隐藏函数

它并不是超级干净,但我已经通过直接包含源文件来测试静态函数,从而启用了静态函数的调用。与TestDept结合,可以将未被测试的内容排除在外,这是相当好的。

代码语言:javascript
复制
 #include "implementation.c"

 /* Now I can call foo(), defined static in implementation.c */

很多C代码都是具有少量测试的遗留代码--在这些情况下,通常更容易添加首先测试代码大部分的集成测试,而不是细粒度的单元测试。这允许您开始将集成测试下的代码重构到一个可测试的单元状态,但根据您的情况,它可能值得也可能不值得投资。当然,您希望能够将单元测试添加到在此期间编写的任何新代码中,因此,有一个可靠的框架并尽早运行是个好主意。

如果您正在使用遗留代码,那么这本书 (有效地使用Michael的遗留代码)是一个很好的读物。

票数 15
EN

Stack Overflow用户

发布于 2013-06-07 01:16:54

这是一个非常好的问题,旨在吸引人们相信C++比C更好,因为它更易于测试。然而,这并不是那么简单。

在编写了大量可测试的C++和C代码以及同样令人印象深刻的不可测试的C++和C代码之后,我可以秘密地说,您可以用这两种语言包装糟糕的不可测试的代码。实际上,上面提到的大多数问题在C++中同样存在问题。例如,许多人用C++编写非对象封装函数并在类中使用它们(参见类中广泛使用C++静态函数,例如MyAscii::fromUtf8 8()类型函数)。

我确信,您已经看到了一个具有太多参数的非常多的C++类函数。如果您认为,仅仅因为一个函数只有一个参数,它更好,那么考虑这样的情况:在内部,它经常使用一组成员变量来掩蔽传入的参数。更不用说“静态或隐藏”函数(提示,记住“私有:”关键字)同样是一个大问题。

所以,你的问题的真正答案不是“因为你所陈述的原因C更糟”,而是“你需要像在C++中一样正确地用C架构它”。例如,如果您有依赖函数,那么将它们放在不同的文件中,并在测试超级函数时通过实现该函数的伪版本返回它们可能提供的答案数。这是勉强能得到的改变。如果要测试静态函数或隐藏函数,请不要这样做。

真正的问题是,您似乎在您的问题中指出,您正在为其他人的库编写您没有编写的测试,并为适当的可测试性进行架构设计。然而,有大量的C++库显示了完全相同的症状,如果您被交给其中一个进行测试,您将同样感到恼火。

解决所有类似问题的方法总是一样的:正确地编写代码,不要使用别人编写不当的代码。

票数 9
EN

Stack Overflow用户

发布于 2013-12-20 13:42:39

当单元测试C时,通常会在测试中包含.c文件,以便在测试公共函数之前先测试静态函数。

如果您有复杂的函数,并且希望测试调用它们的代码,那么就可以使用模拟对象。看看克明卡单元测试框架,它提供了对模拟对象的支持。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/16970035

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档