我这个夏天参与开发了一款使用C语言编写的嵌入式系统。这是一项由我们公司接手的现有项目。我已经习惯了使用JUnit在Java中编写单元测试,但对于需要重构的现有代码以及添加到系统中的新代码,我不知道编写单元测试的最佳方法。
是否存在任何项目可以使像JUnit一样轻松地为C语言编写的代码进行单元测试?特别是针对嵌入式开发(交叉编译到arm-linux平台)的任何见解都将非常感激。
我这个夏天参与开发了一款使用C语言编写的嵌入式系统。这是一项由我们公司接手的现有项目。我已经习惯了使用JUnit在Java中编写单元测试,但对于需要重构的现有代码以及添加到系统中的新代码,我不知道编写单元测试的最佳方法。
是否存在任何项目可以使像JUnit一样轻松地为C语言编写的代码进行单元测试?特别是针对嵌入式开发(交叉编译到arm-linux平台)的任何见解都将非常感激。
在C语言中,一个单元测试框架是Check;C语言中的单元测试框架列表可以在这里找到,并在下面复制。根据您的运行时有多少标准库函数,您可能能够使用其中之一。
AceUnit(高级C和嵌入式单元)是一个舒适的C代码单元测试框架。它试图模仿JUnit 4.x并包括类似反射的能力。AceUnit可以在资源受限制的环境中使用,例如嵌入式软件开发,并且在您无法包含单个标准头文件或从ANSI / ISO C库调用单个标准C函数的环境中运行良好。它还有一个Windows端口。它不使用forks来捕获信号,尽管作者已经表达了添加此功能的兴趣。请参见AceUnit主页。
与Check非常相似,包括分叉以在单独的地址空间中运行单元测试(实际上,Check的原始作者从GNU Autounit借鉴了这个想法)。GNU Autounit广泛使用GLib,这意味着链接等需要特殊选项,但这可能对您来说不是一个大问题,特别是如果您已经在使用GTK或GLib。请参见GNU Autounit主页。
也使用GLib,但不会分叉以保护单元测试的地址空间。
标准C,计划进行Win32 GUI实现。目前不会分叉或以其他方式保护单元测试的地址空间。处于早期开发阶段。请参见CUnit主页。
一个简单的框架,只有一个.c和一个.h文件,您可以将其放入源树中。请参见CuTest主页。
用于C ++的首选单元测试框架; 您也可以使用它来测试C代码。它是稳定的,正在积极开发,并具有GUI界面。不使用CppUnit进行C的主要原因是它非常大,其次您必须使用C ++编译器编写测试。如果这些问题听起来不像关注点,那么一定值得考虑,以及其他C ++单元测试框架。请参见CppUnit主页。
embUnit(嵌入式单元)是另一个用于嵌入式系统的单元测试框架。这个似乎已经被AceUnit取代了。嵌入式单元主页。
一个最小的宏集,仅此而已!重点是展示如何轻松地对代码进行单元测试。请参见MinUnit主页。
一个相当新的CUnit实现,显然仍处于早期开发阶段。请参见CUnit for Mr. Ando主页。
此列表最后更新于2008年3月。
CMocka是一个支持模拟对象的C语言测试框架。它易于使用和设置。
请参见CMocka主页。
Criterion是一个跨平台的C语言单元测试框架,支持自动测试注册、参数化测试、理论测试,并可输出多种格式,包括TAP和JUnit XML。每个测试在其自己的进程中运行,因此可以报告或测试信号和崩溃(如果需要)。
有关更多信息,请参见Criterion主页。
HWUT是一个通用的单元测试工具,对C语言有很好的支持。它可以帮助创建Makefile,在最小的“迭代表”中生成大量的测试用例,沿着状态机走,生成C-stubs等等。总体方法非常独特:判决基于“好的stdout / 坏的stdout”。虽然比较函数是灵活的,但任何类型的脚本都可以用于检查。它可以应用于任何能够产生标准输出的语言。
请参见HWUT主页。
CGreen是一个现代的、便携的、跨语言的C和C++单元测试和模拟框架。它提供了一个可选的BDD表示法,一个模拟库,以及在单个进程中运行它(使调试更容易)。一个测试运行器可以自动发现测试函数。但是你也可以编写自己的程序。
所有这些功能(以及更多)都在CGreen手册中有解释。
维基百科列出了C语言单元测试框架列表。
个人而言,我喜欢Google测试框架。
在测试C代码时真正的困难在于打破对外部模块的依赖,以便将代码隔离成单元。当您尝试在传统代码周围进行测试时,这可能会特别棘手。在这种情况下,我经常使用链接器在测试中使用存根函数。
人们谈论“缝合线”时,就是指这个问题。在C中,您唯一的选择实际上是使用预处理器或链接器来模拟您的依赖项。
我的一个C项目中的典型测试套件可能如下所示:
#include "myimplementationfile.c"
#include <gtest/gtest.h>
// Mock out external dependency on mylogger.o
void Logger_log(...){}
TEST(FactorialTest, Zero) {
EXPECT_EQ(1, Factorial(0));
}
编辑:我已经写了一篇关于单元测试过程式代码的博客文章,并且在GitHub上提供源代码。
编辑:有一本新书即将出版,来自Pragmatic Programmers,专门讲解单元测试C代码,我强烈推荐。
Minunit 是一个极其简单的单元测试框架。我正在使用它来为 AVR 微控制器代码进行单元测试。
我说的和ratkok几乎一样,但如果你在单元测试中嵌入了某些变化,那么...
Unity - 高度推荐用于C语言单元测试的框架。
#include <unity.h>
void test_true_should_be_true(void)
{
TEST_ASSERT_TRUE(true);
}
int main(void)
{
UNITY_BEGIN();
RUN_TEST(test_true_should_be_true);
return UNITY_END();
}
在这个帖子中提到的书《嵌入式C语言测试驱动开发》中的示例是使用Unity(和CppUTest)编写的。
我目前正在使用CuTest单元测试框架:
http://cutest.sourceforge.net/
它非常轻量级和简单,非常适合嵌入式系统。我在目标平台和桌面上使用它没有遇到任何问题。除了编写单元测试之外,只需要:
该系统需要支持堆和一些stdio功能(并非所有嵌入式系统都具备这些功能)。但是代码足够简单,您可以在不具备这些要求的情况下尝试替代方法。
通过谨慎使用extern "C" {}块,它也支持对C ++的测试。
Before
和After
的调用。总的来说,它很可爱。 - Dragas您也许想查看 libtap,这是一个 C 语言测试框架,可以输出“测试任何协议”(Test Anything Protocol,TAP),因此可以与为这项技术开发的各种工具很好地集成。它主要用于动态语言世界,但易于使用并越来越受欢迎。
例如:
#include <tap.h>
int main () {
plan(5);
ok(3 == 3);
is("fnord", "eek", "two different strings not that way?");
ok(3 <= 8732, "%d <= %d", 3, 8732);
like("fnord", "f(yes|no)r*[a-f]$");
cmp_ok(3, ">=", 10);
done_testing();
}
ok(TESTING==IsSimple(), "libtap is super easy to use")
- AShelly有一个名为cmocka的优雅C语言单元测试框架,它支持模拟对象。它只需要标准的C库,在许多计算平台(包括嵌入式)和不同编译器上都可以工作。
它还支持不同的消息输出格式,如Subunit、Test Anything Protocol和jUnit XML报告。
cmocka已经被创建用于在嵌入式平台上工作,并且也支持Windows。
一个简单的测试看起来像这样:
#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <cmocka.h>
/* A test case that does nothing and succeeds. */
static void null_test_success(void **state) {
(void) state; /* unused */
}
int main(void) {
const struct CMUnitTest tests[] = {
cmocka_unit_test(null_test_success),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}
API已经完全记录,源代码中包含多个示例。
如果要开始使用cmocka,则应阅读LWN.net上的文章:在C中使用模拟对象进行单元测试
cmocka 1.0于2015年2月发布。
在测试一个传统的C应用程序时,我并没有取得很大的进展,因此开始寻找一种模拟函数的方法。我非常需要使用模拟函数来使我要测试的C文件与其他文件隔离。我尝试了cmock,并且认为我会采用它。
Cmock扫描头文件并根据其找到的原型生成模拟函数。模拟函数将允许您完全隔离地测试C文件。您所需做的就是将测试文件与模拟函数链接,而不是与真实的对象文件链接。
cmock的另一个优点是,它将验证传递给模拟函数的参数,并允许您指定模拟函数应提供的返回值。这对于测试函数中的不同执行流程非常有用。
测试由典型的testA()和testB()函数组成,在其中构建期望值,调用函数进行测试并检查断言。
最后一步是使用Unity为您的测试生成运行器。 Cmock与Unity测试框架紧密相连。 Unity像任何其他单元测试框架一样容易学习。
非常值得一试,而且相当容易掌握:
http://sourceforge.net/apps/trac/cmock/wiki
更新1
我正在调查的另一个框架是Cmockery。
http://code.google.com/p/cmockery/
它是一个纯C框架,支持单元测试和模拟。它没有依赖于Ruby(与Cmock相反),并且对外部库的依赖非常少。
虽然它不会生成代码,但需要一些手动工作来设置模拟函数。这对于现有项目来说并不需要太多的工作,因为原型不会经常更改:一旦您拥有了模拟函数,您就不需要长时间更改它们(这是我的情况)。额外的输入提供完全控制模拟函数。如果您不喜欢某些东西,只需更改模拟函数即可。
不需要特殊的测试运行程序。你只需要创建一个测试数组并将其传递给run_tests函数即可。这里需要做更多的手动工作,但我绝对喜欢这种自包含自治框架的想法。assert()
和&&
来传递消息,没有任何外部依赖。如果有人习惯了完整的堆栈测试框架,那么这可能不太适合 :)assert
而无需任何其他库或框架就能实现。我认为如果你是一名新手,这可能是一个很好的起点。 - kabirbaidhya