unit testingC代码
今年夏天,我在一个embedded式系统上编写了C语言程序。这是我工作的公司现有的一个项目。 我已经习惯于使用JUnit编写Javaunit testing,但对于为现有代码(需要重构)编写unit testing以及添加到系统中的新代码的最佳方法而言,已经不知所措。
有没有什么办法让unit testing纯C代码就像unit testingJava代码一样简单,例如JUnit ? 任何专门适用于embedded式开发(交叉编译到arm-linux平台)的洞察力将不胜感激。
C中的一个unit testing框架是Check ; C语言中的unit testing框架列表可以在这里find,现转载如下。 根据运行时有多less个标准库函数,您可能不能使用其中的一个。
AceUnit
AceUnit(高级C和embedded式单元)自称是一个舒适的C代码unit testing框架。 它试图模仿JUnit 4.x并包含类似reflection的function。 AceUnit可用于资源约束环境,例如embedded式软件开发,重要的是,它可以在不能包含单个标准头文件的环境中运行,并且无法从ANSI / ISO C库调用单个标准C函数。 它也有一个Windows端口。 虽然作者表示有兴趣添加这样的function,但它并不使用叉子来捕捉信号。 查看AceUnit主页 。
GNU Autounit
与Check大致相同,包括在单独的地址空间中运行unit testing(实际上,Check的原始作者借鉴了GNU Autounit的思想)。 GNU Autounit广泛使用GLib,这意味着链接等需要特殊的选项,但是这可能不是一个大问题,特别是如果您已经使用GTK或GLib。 请参阅GNU Autounit主页 。
库尼特
也使用GLib,但不分叉来保护unit testing的地址空间。
库尼特
标准C,计划用于Win32 GUI实现。 目前没有分叉或以其他方式保护unit testing的地址空间。 在早期发展。 查看CUnit主页 。
可爱
一个只有一个.c和一个.h文件的简单框架,可以放到源代码树中。 参见CuTest主页 。
CppUnit的
C ++的首要unit testing框架; 你也可以用它来testingC代码。 它稳定,积极开发,并有一个GUI界面。 不使用CppUnit for C的主要原因是首先它是相当大的,其次你必须用C ++编写你的testing,这意味着你需要一个C ++编译器。 如果这听起来不是问题,那么与其他C ++unit testing框架一起值得考虑。 请参阅CppUnit主页 。
embUnit
embUnit(embedded式单元)是embedded式系统的另一个unit testing框架。 这个似乎被AceUnit所取代。 embedded式单元主页 。
MinUnit
一组最小的macros,就是这样! 关键是要展示如何简单的unit testing你的代码。 请参阅MinUnit主页 。
CU先生安藤先生
一个CUnit的实现是相当新的,显然还处于早期的开发阶段。 查看Ando主页的CUnit 。
这个清单最后在2008年3月更新。
其他:
CMocka
CMocka是C的testing框架,支持模拟对象。 这很容易使用和设置。 CMocka官方主页。
标准
Criterion是一个跨平台的Cunit testing框架,支持自动testing注册,参数化testing,理论,并且可以输出到多种格式,包括TAP和JUnit XML。 每个testing都在自己的过程中运行,因此可以根据需要报告或testing信号和崩溃。 有关更多信息,请参阅Criterion主页 。
HWUT
HWUT是一个通用的unit testing工具,对C语言有很好的支持。它可以帮助创buildMakefiles,生成大量的用最小的“迭代表”编码的testing用例,沿着状态机行走,生成C-stub等等。 一般的做法是非常独特的:判决是基于“好标准输出/坏标准输出”。 比较function虽然灵活, 因此,可以使用任何types的脚本来检查。 它可能适用于任何可以产生标准输出的语言。 见HWUT主页。
我个人喜欢Googletesting框架 。
testingC代码的真正困难在于打破了外部模块的依赖关系,因此您可以用单位隔离代码。 当您试图围绕遗留代码进行testing时,这可能尤其成问题。 在这种情况下,我经常发现自己使用链接器在testing中使用存根函数。
这是人们谈论“ 接缝 ”时所指的。 在C中,唯一的select是使用预处理器或链接器来嘲笑你的依赖关系。
我的一个C项目中的典型testing套件可能如下所示:
#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)); }
请注意,您实际上包括C文件而不是头文件 。 这给了访问所有静态数据成员的好处。 在这里,我嘲笑我的logging器(可能在logger.o中并给出一个空的实现,这意味着testing文件独立于代码库的其余部分进行编译和链接,并独立执行。
至于交叉编译的代码,为了这个工作,你需要在目标上的良好设施。 我已经在PowerPC架构上用googletest交叉编译到Linux。 这是有道理的,因为你有一个完整的shell和os来收集你的结果。 对于较less丰富的环境(我将它们分类为没有完整操作系统的任何东西),您应该在主机上构build并运行。 你应该这样做,所以你可以作为构build的一部分自动运行testing。
我发现testingC ++代码通常要容易得多,因为OO代码通常比程序更less耦合(当然,这很大程度上取决于编码风格)。 同样在C ++中,你可以使用像dependency injection和方法覆盖这样的技巧来将代码封装起来。
迈克尔羽毛有一本关于testing遗留代码的优秀书籍 。 在一章中,他介绍了处理非OO代码的技术,我强烈build议。
编辑 :我写了一篇关于unit testing程序代码的博客文章 , 在GitHub上提供了源代码。
编辑 :有一个新的书从实用程序员出来 ,专门处理unit testingC代码, 我强烈build议 。
Minunit是一个令人难以置信的简单的unit testing框架。 我正在使用它来unit testingavr的c微控制器代码。
我目前正在使用CuTestunit testing框架:
http://cutest.sourceforge.net/
它非常适合embedded式系统,因为它非常轻巧,简单。 在目标平台上以及在桌面上运行我都没有问题。 除了编写unit testing之外,所有需要的是:
- 包括无论你在哪里调用CuTest例程的头文件
- 一个额外的'C'文件被编译/链接到图像
- 一些简单的代码添加到主要build立和调用unit testing – 我只是有一个特殊的main()函数,如果UNITTEST是在编译过程中被编译的编译。
系统需要支持堆和一些stdiofunction(不是所有的embedded式系统都有)。 但是代码很简单,如果你的平台没有这些代码,你可能可以替代这些需求。
通过一些明智的使用extern“C”{}块,它也支持testingC ++就好了。
我说几乎与ratkok一样,但如果你有一个embedded式扭曲的unit testing,然后…
Unity – unit testingC代码的强烈推荐框架。
本书中提到的这个线程TDD中embedded式C的例子是用Unity(和CppUTest)编写的。
你也可能想看看libtap ,一个Ctesting框架,它输出testing任意协议(TAP),并且与这个技术出现的各种工具很好地集成在一起。 它主要用于dynamic语言世界,但易于使用和变得非常stream行。
一个例子:
#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*[af]$"); cmp_ok(3, ">=", 10); done_testing(); }
C有一个优雅的unit testing框架,支持名为cmocka的模拟对象。 它只需要标准的C库,可以在各种计算平台(包括embedded式)和不同的编译器上工作。
它还支持不同的消息输出格式,如Subunit,Test Anything Protocol和jUnit XML报告。
cmocka已经被创build,可以在embedded式平台上运行,并且也支持Windows。
一个简单的testing看起来像这样:
#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中的模拟对象进行unit testing
cmocka 1.0已于2015年2月发布。
在开始寻找一种模拟函数的方法之前,我没有太多的testing传统的C应用程序。 我非常需要模拟来隔离我想testing的C文件。 我给了cmock一个尝试,我想我会采纳它。
Cmock扫描头文件并根据find的原型生成模拟函数。 嘲笑将允许您完全隔离testing一个C文件。 你所要做的就是把你的testing文件和mock链接起来,而不是真正的目标文件。
cmock的另一个优点是它将validation传递给模拟函数的参数,并且可以让你指定模拟应该提供的返回值。 这对testing函数中不同的执行stream程非常有用。
testing包括您build立期望的典型testA(),testB()函数,调用函数来testing和检查断言。
最后一步是用统一来为你的testing生成一个跑步者。 Cmock绑定到统一testing框架。 Unity和其他unit testing框架一样简单易学。
值得一试,相当容易掌握:
http://sourceforge.net/apps/trac/cmock/wiki
更新1
我正在研究的另一个框架是Cmockery。
http://code.google.com/p/cmockery/
它是一个纯粹的C语言框架,支持unit testing和嘲笑。 它不依赖于ruby(与Cmock相反),并且对外部库很less依赖。
它需要更多的手动工作来设置模拟,因为它不生成代码。 对于现有的项目来说,这并不代表很多工作,因为原型不会有太大的改变:一旦有了你的模拟,你将不需要改变它们一段时间(这是我的情况)。 额外的打字提供了完全控制嘲笑。 如果有什么你不喜欢的,你只需改变你的模拟。
不需要专门的testing者。 您只需要创build一个testing数组并将其传递给run_tests函数。 这里还有更多的手工工作,但我绝对喜欢一个自包含的自治框架的想法。
另外它包含一些漂亮的C技巧,我不知道。
总的来说,Cmockery需要更多的了解mock才能开始。 例子应该帮助你克服这一点。 看起来它可以用更简单的机制来完成这项工作。
有CUnit
embedded式单元是embedded式C系统的unit testing框架。 它的devise是从JUnit和CUnit等复制而来,然后在某种程度上适用于embedded式C系统。 embedded式单元不需要std C库。 所有对象都分配给const区域。
Tessy自动化embedded式软件的unit testing。
作为C新手,我发现在C中称为Test driven development的幻灯片非常有帮助。 基本上,它使用标准的assert()
和&&
来传递消息,没有任何外部依赖。 如果有人习惯于一个完整的堆栈testing框架,这可能不会做:)
Michael Feather的“使用遗留代码有效地工作”一书介绍了在C开发过程中针对unit testing的大量技术。
有些dependency injection技术与C相关,这是我从未见过的。
我不使用框架,我只是使用autotools“检查”目标支持。 实现一个“main”并使用assert(s)。
我的testing目录Makefile.am(s)看起来像:
check_PROGRAMS = test_oe_amqp test_oe_amqp_SOURCES = test_oe_amqp.c test_oe_amqp_LDADD = -L$(top_builddir)/components/common -loecommon test_oe_amqp_CFLAGS = -I$(top_srcdir)/components/common -static TESTS = test_oe_amqp
我们写了CHEAT (托pipe在GitHub上 ),以便于使用和移植。
它没有依赖关系,不需要安装或configuration。 只需要一个头文件和一个testing用例。
#include <cheat.h> CHEAT_TEST(mathematics_still_work, cheat_assert(2 + 2 == 4); cheat_assert_not(2 + 2 == 5); )
testing编译成一个可执行文件,负责运行testing并报告结果。
$ gcc -I . tests.c $ ./a.out .. --- 2 successful of 2 run SUCCESS
它也有漂亮的颜色。
CppUTest – 高度推荐的unit testingC代码框架。
本书中提到的这个线程TDD中embedded式C的例子是用CppUTest编写的。
我使用CxxTest作为embedded式c / c ++环境(主要是C ++)。
我更喜欢CxxTest,因为它有一个perl / python脚本来构buildtesting运行器。 经过一个小小的坡度,让它设置(小,因为你不必编写testing运行器),它很容易使用(包括样本和有用的文档)。 大部分工作是设置代码访问的“硬件”,所以我可以有效地进行单元/模块testing。 之后,添加新的unit testing用例很容易。
如前所述,它是一个C / C ++unit testing框架。 所以你需要一个C ++编译器。
CxxTest用户指南 CxxTest Wiki
在读完Minunit之后,我认为一个更好的方法是把testing的基础放在macros观上,我使用很多像防御性编程技术。 所以我用Minunit与标准assert混合的想法。 你可以在k0ga的博客中看到我的框架(一个好名字可能是NoMinunit)
Cmockery是最近推出的Google Code项目,它包含一个非常简单易用的用于编写unit testing的C库。
http://code.google.com/p/cmockery/上的; cmockery
Google拥有出色的testing框架。 http://code.google.com/p/googletest/wiki/GoogleTestPrimer
是的,据我所知,它将与纯C,即不需要C ++function(可能需要C ++编译器,不知道)。
首先,看看这里: http : //en.wikipedia.org/wiki/List_of_unit_testing_frameworks#C
我的公司有我们客户使用的C库。 我们使用CxxTest(一个C ++unit testing库)来testing代码。 CppUnit也将工作。 如果你被困在C中,我会推荐RCUNIT(但是CUnit也不错)。
在testing目标之前,我使用RCUNIT在PC上对embedded代码进行了一些unit testing。 良好的硬件接口抽象是很重要的,否则sorting和内存映射寄存器将会杀死你。
试试lcut! – http://code.google.com/p/lcut
如果你熟悉JUnit,那么我推荐CppUnit。 http://cppunit.sourceforge.net/cppunit-wiki
假设你有c ++编译器来做unit testing。 如果不是的话,我不得不同意亚当·罗森菲尔德的看法是你想要的。
一种使用的方法是用C ++ xUnit框架(和C ++编译器)开发unit testing代码,同时将目标系统的源代码保存为C模块。
确保你经常在你的交叉编译器下编译你的C源代码,如果可能的话,自动进行unit testing。
LibU( http://koanlogic.com/libu )有一个unit testing模块,它允许明确的testing套件/案例依赖性,testing隔离,并行执行和可定制的报告格式(默认格式是xml和txt)。
该库是BSD许可的,并包含许多其他有用的模块 – networking,debugging,常用的数据结构,configuration等 – 你应该在项目中需要它们…
我很惊讶,没有人提到Cutter(http://cutter.sourceforge.net/)你可以testingC和C ++,它与自动工具无缝集成,并有一个非常好的教程可用。
API Sanity Checker – C / C ++库的testing框架:
一个共享C / C ++库的基本unit testing的自动生成器。 它能够为参数生成合理的(大多数但不是全部)input数据,并通过分析头中的声明为API中的每个函数构造简单的(“完整”或“浅”质量)testing用例文件。
生成的testing质量允许在简单用例中检查是否存在严重错误。 该工具能够build立和执行生成的testing,并检测崩溃(segfaults),中止,各种发射信号,非零的程序返回码和程序挂起。
例子:
- fontconfig 2.8.0的testing套件
- FreeType 2.4.8的testing套件
如果你的目标是Win32平台或NT内核模式,你应该看看cfix 。
如果你仍然在寻找testing框架, CUnitWin32是Win32 / NT平台的一个。
这解决了我面临的其他testing框架的一个基本问题。 即全局/静态variables处于确定性状态,因为每个testing都是作为一个单独的过程执行的。