我今年夏天用C语言编写了一个嵌入式系统。这是我工作的公司接手的一个现有项目。我已经非常习惯于使用JUnit在Java中编写单元测试,但是对于为现有代码(需要重构)以及添加到系统中的新代码编写单元测试的最佳方法感到困惑。
是否有任何项目可以像使用JUnit对Java代码进行单元测试那样简单地对普通C代码进行单元测试?非常感谢任何专门用于嵌入式开发(对arm-linux平台进行交叉编译)的见解。
我今年夏天用C语言编写了一个嵌入式系统。这是我工作的公司接手的一个现有项目。我已经非常习惯于使用JUnit在Java中编写单元测试,但是对于为现有代码(需要重构)以及添加到系统中的新代码编写单元测试的最佳方法感到困惑。
是否有任何项目可以像使用JUnit对Java代码进行单元测试那样简单地对普通C代码进行单元测试?非常感谢任何专门用于嵌入式开发(对arm-linux平台进行交叉编译)的见解。
Answers:
C语言中的一个单元测试框架是Check ; 可以在这里找到C语言中的单元测试框架列表,并在下面复制。根据您的运行时具有多少标准库函数,您是否可以使用其中之一。
AceUnit
AceUnit(高级C和嵌入式单元)将自己视为一个舒适的C代码单元测试框架。它试图模仿JUnit 4.x,并包括类似反射的功能。AceUnit可用于资源限制环境(例如嵌入式软件开发)中,并且重要的是,它在无法包含单个标准头文件且无法从ANSI / ISO C库调用单个标准C函数的环境中也能正常运行。它还具有Windows端口。尽管作者已经表示有兴趣添加这种功能,但它不使用分叉来捕获信号。请参阅AceUnit主页。
GNU Autounit
与Check大致相同,包括分叉在单独的地址空间中运行单元测试(实际上,Check的原始作者是从GNU Autounit借用此想法的)。GNU Autounit广泛使用GLib,这意味着链接等需要特殊的选项,但这对您来说不是一个大问题,特别是如果您已经在使用GTK或GLib。参见GNU Autounit主页。
单位
也使用GLib,但不分叉以保护单元测试的地址空间。
单位
标准C,带有Win32 GUI实现的计划。当前不分叉或以其他方式保护单元测试的地址空间。在早期开发中。请参阅CUnit主页。
铜测试
一个只有一个.c和一个.h文件的简单框架,您将其放入源代码树中。请参阅CuTest主页。
CppUnit
一流的C ++单元测试框架;您还可以使用它来测试C代码。它稳定,积极开发,并具有GUI界面。不使用CppUnit for C的主要原因是,它首先很大,其次,您必须使用C ++编写测试,这意味着您需要C ++编译器。如果这些听起来不令人担忧,那么与其他C ++单元测试框架一起绝对值得考虑。请参阅CppUnit主页。
embUnit
embUnit(嵌入式单元)是嵌入式系统的另一个单元测试框架。这似乎已被AceUnit取代。嵌入式单元主页。
最小单位
最小的宏集就可以了!关键是要表明对代码进行单元测试有多么容易。参见MinUnit主页。
安藤先生的CUnit
这是一个相当新的CUnit实现,并且显然仍在早期开发中。有关Ando先生的主页,请参见CUnit。
此列表的最新更新时间为2008年3月。
CMocka是C的测试框架,支持模拟对象。易于使用和设置。
请参阅CMocka主页。
Criterion是跨平台的C单元测试框架,支持自动测试注册,参数化测试,理论,并且可以输出为多种格式,包括TAP和JUnit XML。每个测试都在其自己的过程中运行,因此可以根据需要报告或测试信号和崩溃。
有关更多信息,请参见Criterion主页。
HWUT是具有C的强大支持的通用单元测试工具。它可以帮助创建Makefile,生成用最小的“迭代表”编码的大量测试用例,遍历状态机,生成C存根等等。通用方法非常独特:裁决基于“好的标准输出/不好的标准输出”。但是,比较功能很灵活。因此,任何类型的脚本都可以用于检查。它可以应用于可以产生标准输出的任何语言。
请参阅HWUT主页。
适用于C和C ++的现代,可移植,跨语言单元测试和模拟框架。它提供了可选的BDD表示法,模拟库以及在单个进程中运行它的能力(使调试更加容易)。可以自动发现测试功能的测试运行程序。但是您可以以编程方式创建自己的。
所有这些功能(以及更多功能)在CGreen手册中进行了说明。
Wikipedia在“单元测试框架列表:C”下给出了C单元测试框架的详细列表。
0.11.0
于2016年12月17日发布。
我个人喜欢Google Test框架。
测试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));
}
请注意,您实际上包括的是C文件,而不是头文件。这具有访问所有静态数据成员的优势。在这里,我模拟了我的记录器(可能在logger.o中,并提供了一个空的实现。这意味着测试文件独立于其余代码库进行编译和链接,并独立执行。
至于交叉编译代码,要使其正常工作,您需要在目标设备上具有良好的功能。我通过在PowerPC架构上编译为Linux的googletest cross来完成此任务。这是有道理的,因为那里有一个完整的shell和os来收集结果。对于不太丰富的环境(我将其归类为没有完整操作系统的任何东西),您应该仅在主机上构建并运行。无论如何,您都应该这样做,以便您可以在构建过程中自动运行测试。
我发现测试C ++代码通常要容易得多,这是因为OO代码通常比过程耦合少(当然,这在很大程度上取决于编码样式)。同样,在C ++中,您可以使用诸如依赖注入和方法重写之类的技巧来将接缝插入以其他方式封装的代码中。
迈克尔·费瑟斯(Michael Feathers)有一本关于测试遗留代码的好书。在第一章中,他介绍了我强烈推荐的处理非OO代码的技术。
编辑:我写了一篇有关单元测试过程代码的博客文章,其源代码可以在GitHub上找到。
Minunit是一个非常简单的单元测试框架。我正在使用它对AVR的C微控制器代码进行单元测试。
我目前正在使用CuTest单元测试框架:
http://cutest.sourceforge.net/
它非常轻巧和简单,因此非常适合嵌入式系统。使它在目标平台以及台式机上都能正常工作没有问题。除了编写单元测试之外,还需要:
系统需要支持堆和某些stdio功能(并非所有嵌入式系统都具有)。但是代码很简单,如果您的平台没有这些需求,您可能可以替代这些需求。
明智地使用extern“ C” {}块,它也支持测试C ++。
Before
和After
打电话。总而言之,它很可爱。
我说的和ratkok几乎一样,但是如果您对单元测试有内在的扭曲,那么...
Unity-强烈建议对C代码进行单元测试的框架。
您可能还想看看libtap,这是一个C测试框架,可以输出“测试任何协议”(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")
有一个优雅的C单元测试框架,它支持称为cmocka的模拟对象。它只需要标准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扫描头文件并根据找到的原型生成模拟函数。Mocks可以让您完美隔离地测试C文件。您要做的就是将测试文件与模拟链接而不是真实的目标文件链接。
cmock的另一个优点是它将验证传递给模拟函数的参数,并使您可以指定模拟应提供的返回值。这对于测试函数中不同的执行流程非常有用。
测试由典型的testA()和testB()函数组成,您可以在其中构建期望,调用函数以测试和检查断言。
最后一步是统一生成测试的运行程序。Cmock与统一测试框架相关。Unity与其他任何单元测试框架一样易于学习。
值得一试,很容易掌握:
http://sourceforge.net/apps/trac/cmock/wiki
更新1
我正在研究的另一个框架是Cmockery。
http://code.google.com/p/cmockery/
它是一个纯C框架,支持单元测试和模拟。它与ruby无关(与Cmock相反),对外部库的依赖性很小。
因为它不会生成代码,所以需要更多的手动工作来设置模拟。因为原型不会发生太大变化,所以对于现有项目而言,这并不代表太多工作:一旦有了模拟,就无需在一段时间内进行更改(这就是我的情况)。额外的输入可以完全控制模拟。如果您不喜欢某些东西,只需更改您的模拟即可。
无需特殊的测试跑步者。您只需要创建一个测试数组并将其传递给run_tests函数。这里也需要更多的手动工作,但是我绝对喜欢一个独立的自治框架的想法。
另外,它包含一些我不知道的漂亮C技巧。
总体而言,Cmockery需要对模拟有更多的了解才能上手。示例可以帮助您克服这一问题。看起来它可以用更简单的机制完成任务。
作为C的新手,我发现C中名为“ 测试驱动的开发”的幻灯片非常有用。基本上,它与标准assert()
一起使用&&
以传递消息,而没有任何外部依赖性。如果有人习惯了完整的堆栈测试框架,则可能不会:)
assert
无需任何其他库或框架就可以遵循。我认为,如果您只是新手,这可能是一个起点。
而嵌入式设备是嵌入式C系统的单元测试框架。它的设计从JUnit和CUnit等复制而来,然后在某种程度上适用于Embedded C System。嵌入式单元不需要std C库。所有对象都分配给const区域。
而苔丝自动化嵌入式软件的单元测试。
embunit
,对此感到失望。
我不使用框架,仅使用自动工具的“检查”目标支持。实现一个“主”并使用断言。
我的测试目录Makefile.am看起来像:
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
迈克尔·费瑟(Michael Feather)的书“有效地使用旧版代码”介绍了许多C开发过程中特定于单元测试的技术。
有一些与依赖注入有关的技术,这些技术特定于C,而我在其他任何地方都没有看到过。
我将CxxTest用于嵌入式c / c ++环境(主要是C ++)。
我更喜欢CxxTest,因为它有一个perl / python脚本来构建测试运行器。经过一小段坡度即可进行设置(由于不必编写测试运行程序,因此尺寸较小),它非常易于使用(包括示例和有用的文档)。最多的工作是设置代码访问的“硬件”,这样我就可以有效地进行单元/模块测试。之后,很容易添加新的单元测试用例。
如前所述,它是一个C / C ++单元测试框架。因此,您将需要C ++编译器。
Google具有出色的测试框架。https://github.com/google/googletest/blob/master/googletest/docs/primer.md
是的,据我所知,它将与纯C一起使用,即不需要C ++功能(不确定,可能需要C ++编译器)。
首先,请看这里:http : //en.wikipedia.org/wiki/List_of_unit_testing_frameworks#C
我公司有一个供客户使用的C库。我们使用CxxTest(一个C ++单元测试库)来测试代码。CppUnit也将起作用。如果您陷在C中,我建议您使用RCUNIT(但CUnit也不错)。
如果您熟悉JUnit,则建议使用CppUnit。 http://cppunit.sourceforge.net/cppunit-wiki
那是假设您有c ++编译器来进行单元测试。如果不是这样的话,我必须同意亚当·罗森菲尔德的观点,那就是要检查。
尝试lcut!- http://code.google.com/p/lcut
API Sanity Checker — C / C ++库的测试框架:
共享的C / C ++库的基本单元测试的自动生成器。通过分析标头中的声明,它能够为参数生成合理的(在大多数情况下,但不是所有情况下)输入数据,并为API中的每个函数编写简单的(“合理”或“浅”质量)测试用例。文件。
生成的测试的质量允许检查简单用例中是否存在严重错误。该工具能够构建和执行生成的测试,并检测崩溃(段错误),中止,各种发出的信号,非零程序返回代码和程序挂起。
例子:
LibU(http://koanlogic.com/libu)具有一个单元测试模块,该模块允许显式的测试套件/案例依赖性,测试隔离,并行执行和可自定义的报告格式化程序(默认格式为xml和txt)。
该库已获得BSD许可,并且包含许多其他有用的模块-网络,调试,常用数据结构,配置等-如果您在项目中需要它们,则...
令我惊讶的是,没有人提到Cutter(http://cutter.sourceforge.net/)。 您可以测试C和C ++,它与自动工具无缝集成,并且提供了非常不错的教程。
如果您仍在寻找测试框架,则CUnitWin32是Win32 / NT平台的一种。
这解决了我在其他测试框架中遇到的一个基本问题。也就是说,全局/静态变量处于确定状态,因为每个测试都作为单独的过程执行。