单元测试C代码

今年夏天,我开发了一个用纯C语言编写的嵌入式系统。这是我工作的公司接管的一个现有项目。我已经非常习惯于使用JUnit在Java编写单元测试,但不知道为现有代码(需要重构)以及添加到系统中的新代码编写单元测试的最佳方式。

是否有任何项目可以使单元测试纯C代码像使用JUnit进行单元测试Java代码一样简单?任何专门适用于嵌入式开发(交叉编译到arm-linux平台)的见解都将不胜感激。

507955 次浏览

C中的一个单元测试框架是检查;C中的单元测试框架列表可以在这里中找到,并在下面复制。根据您的运行时有多少个标准库函数,您可能能够或不能使用其中一个。

AceUnit

AceUnit(Advanced C and Embedded Unit)自称是一个舒适的C代码单元测试框架。它试图模仿JUnit 4. x,并包含类似反射的功能。AceUnit可用于资源约束的环境,例如嵌入式软件开发,重要的是,它在不能包含单个标准头文件并且不能从ANSI/ISO C库调用单个标准C函数的环境中运行良好。它也有一个Windows端口。它不使用分叉来捕获信号,尽管作者表示有兴趣添加这样的功能。参见AceUnit主页

GNU Autounit

与Check类似,包括分叉在单独的地址空间中运行单元测试(事实上,Check的原始作者借用了GNU Autounit的想法)。GNU Autounit广泛使用GLib,这意味着链接等需要特殊选项,但这对您来说可能不是大问题,特别是如果您已经在使用GTK或GLib。请参阅GNU Autounit主页

cUnit

也使用GLib,但不分叉以保护单元测试的地址空间。

CUnit

标准C,带有Win32 GUI实现计划。当前不分叉或以其他方式保护单元测试的地址空间。在早期开发中。请参阅CUnit主页

CuTest

一个简单的框架,只有一个. c和一个. h文件,您可以放入源代码树中。请参阅CuTest主页

CppUnit

C++首屈一指的单元测试框架;你也可以用它来测试C代码。它很稳定,开发活跃,并且有一个GUI界面。不使用CppUnit for C的主要原因首先是它相当大,其次你必须用C++编写测试,这意味着你需要一个C++的编译器。如果这些听起来不像是问题,它绝对值得考虑,以及其他C++单元测试框架。参见CppUnit主页

embUnit

Embedded Unit(Embedded Unit)是嵌入式系统的另一个单元测试框架。

MinUnit

一组最小的宏,仅此而已!重点是展示对代码进行单元测试是多么容易。参见MinUnit主页

CUnit找Ando先生

一个相当新的CUnit实现,显然仍处于早期开发阶段。参见安藤先生的主页

这份名单最近一次更新是在2008年3月。

更多框架:

CMocka

CMocka是一个支持模拟对象的C测试框架。它易于使用和设置。

CMocka主页

标准

一个跨平台的C单元测试框架,支持自动测试注册、参数化测试、理论,可以输出为多种格式,包括TAP和JUnit XML。每个测试都在自己的进程中运行,因此可以在需要时报告或测试信号和崩溃。

有关更多信息,请参阅标准主页

HWUT

HWUT是一个通用的单元测试工具,对C有很好的支持。它可以帮助创建Makefile,生成以最小的“迭代表”编码的大量测试用例,沿着状态机遍历,生成C存根等等。一般的方法非常独特:判决基于“好标准输出/坏标准输出”。不过,比较函数很灵活。因此,任何类型的脚本都可用于检查。它可以应用于任何可以产生标准输出的语言。

HWUT主页

CGreen

用于C和C++的现代、可移植、跨语言单元测试和模拟框架。它提供了可选的BDD符号、模拟库以及在单个进程中运行它的能力(使调试更容易)。可以使用自动发现测试函数的测试运行程序。但您可以以编程方式创建自己的。

所有这些特性(以及更多)都在cGreen手册中解释。

Wikipedia给出了单元测试框架列表:C下的C单元测试框架的详细列表

Google拥有出色的测试框架。https://github.com/google/googletest/blob/master/googletest/docs/primer.md

是的,据我所知,它适用于纯C,即不需要C++功能(可能需要C++编译器,不确定)。

如果您熟悉JUnit,那么我推荐CppUnit。http://cppunit.sourceforge.net/cppunit-wiki

这是假设你有c++编译器来做单元测试。如果没有,那么我必须同意Adam Rosenfield的观点,即检查是你想要的。

使用的一种技术是使用C++xUnit框架(和C++编译器)开发单元测试代码,同时将目标系统的源代码保持为C模块。

确保在交叉编译器下定期编译C源代码,如果可能的话,使用单元测试自动编译。

C单元

嵌入式单元是嵌入式C系统的单元测试框架。它的设计是从JUnit和CUnit等处复制的,然后在一定程度上适应了嵌入式C系统。嵌入式单元不需要std C库。所有对象都分配给const区域。

Tess y自动化了嵌入式软件的单元测试。

MinUnit是一个非常简单的单元测试框架。我用它来单元测试c微控制器代码avr.

首先,看这里:http://en.wikipedia.org/wiki/List_of_unit_testing_frameworks#C

我的公司有一个客户使用的C库。我们使用CxxTest(一个C++单元测试库)来测试代码。CppUnit也可以。如果你被困在C中,我会推荐RCUNIT(但CUnit也很好)。

我将CxxTest用于嵌入式c/c++环境(主要是C++)。

我更喜欢CxxTest,因为它有一个perl/python脚本来构建测试运行器。经过一个小斜率的设置(更小,因为你不必编写测试运行器),它很容易使用(包括示例和有用的留档)。大部分工作是设置代码访问的“硬件”,这样我就可以有效地进行单元/模块测试。之后很容易添加新的单元测试用例。

如前所述,它是一个C/C++单元测试框架。所以你需要一个C++编译器。

CxxTest用户指南测试知识库

您可能还想看看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();}

我不使用框架,我只是使用自动工具“检查”目标支持。实现一个“主”并使用断言。

我的测试目录Makefile.am(S)看起来像:

check_PROGRAMS = test_oe_amqp
test_oe_amqp_SOURCES = test_oe_amqp.ctest_oe_amqp_LDADD = -L$(top_builddir)/components/common -loecommontest_oe_amqp_CFLAGS = -I$(top_srcdir)/components/common -static
TESTS = test_oe_amqp

嘲弄是一个最近启动的项目,它包含一个非常简单的使用C库来编写单元测试。

我目前正在使用CuTest单元测试框架:

http://cutest.sourceforge.net/

它非常适合嵌入式系统,因为它非常轻量级和简单。让它在目标平台和桌面上工作没有问题。除了编写单元测试之外,所需要的只是:

  • 包含在任何位置的头文件你在调用CuTest例程
  • 一个额外的'C'文件编译/链接到图像
  • 一些简单的代码添加到main to设置并调用单元测试-我只是在一个特殊的主()编译的函数,如果在定义的过程中构建。

系统需要支持堆和一些标准输入输出功能(不是所有嵌入式系统都有),但是代码很简单,如果你的平台没有这些功能,你可以用它们来替代这些需求。

通过明智地使用extern“C”{}块,它还支持测试C++很好。

如果您的目标是Win32平台或NT内核模式,您应该看看cFix

在对目标进行测试之前,我使用RCUNIT对PC上的嵌入式代码进行了一些单元测试。良好的硬件接口抽象很重要,否则endianness和内存映射寄存器会杀了你。

如果你还在寻找测试框架,CUnitWin32是一个适用于Win32/NT平台的框架。

这解决了我在其他测试框架中面临的一个基本问题。即全局/静态变量处于确定性状态,因为每个测试都作为一个单独的过程执行。

我喜欢google test框架

测试C代码的真正困难在于打破对外部模块的依赖,这样你就可以以单元隔离代码。当你试图围绕遗留代码进行测试时,这可能会特别成问题。在这种情况下,我经常发现自己使用链接器在测试中使用存根函数。

这就是人们在谈论“接缝”时所指的。在C中,您唯一的选择实际上是使用预处理器或链接器来模拟您的依赖项。

我的一个C项目中的典型测试套件可能如下所示:

#include "myimplementationfile.c"#include <gtest/gtest.h>
// Mock out external dependency on mylogger.ovoid Logger_log(...){}
TEST(FactorialTest, Zero) {EXPECT_EQ(1, Factorial(0));}

请注意,您实际上包含的是C文件而不是头文件。这提供了访问所有静态数据成员的优势。在这里我模拟了我的记录器(可能在logger. o中并给出一个空实现。这意味着测试文件独立于代码库的其余部分进行编译和链接,并独立执行。

至于交叉编译代码,要做到这一点,你需要在目标上有良好的设施。我已经在PowerPC架构上使用googletest交叉编译Linux做到了这一点。这是有道理的,因为你有一个完整的shell和操作系统来收集你的结果。对于不太丰富的环境(我将其归类为没有完整操作系统的任何东西),你应该只在主机上构建和运行。你应该这样做,这样你就可以作为构建的一部分自动运行测试。

我发现测试C++代码通常要容易得多,因为OO代码通常比过程代码耦合少得多(当然这在很大程度上取决于编码风格)。此外,在C++你可以使用依赖注入和方法覆盖等技巧来接缝其他封装的代码。

Michael Feathers有一个关于测试遗留代码的优秀书籍。在一章中,他介绍了处理非OO代码的技术,我强烈推荐。

编辑:我用GitHub上提供的源代码编写了关于单元测试过程代码的博客文章

编辑:有一个实用程序员的新书问世专门解决单元测试C代码我强烈推荐

Michael Feather的书“有效地使用遗留代码”介绍了许多特定于C开发期间的单元测试的技术。

有一些与依赖注入相关的技术是特定于C的,我在其他任何地方都没有见过。

LibU(http://koanlogic.com/libu)有一个单元测试模块,允许显式测试套件/案例依赖关系、测试隔离、并行执行和可定制的报告格式化程序(默认格式是xml和txt)。

该库是BSD许可的,包含许多其他有用的模块-网络,调试,常用的数据结构,配置等-如果您在项目中需要它们…

除了我明显的偏见

http://code.google.com/p/seatest/

是一个很好的简单方法来单元测试C代码. mimics xUnit

在我开始寻找模拟函数的方法之前,我没有深入测试遗留的C应用程序。我非常需要模拟来将我想测试的C文件与其他文件隔离开来。我尝试了cmock,我想我会采用它。

Cmock扫描头文件并根据它找到的原型生成模拟函数。Mocks将允许您在完美隔离的情况下测试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函数。这里也有更多的手动工作,但我绝对喜欢自包含自治框架的想法。

此外,它还包含一些我不知道的漂亮的C技巧。

总体而言,Cmockery需要对模拟有更多的了解才能开始。示例应该可以帮助您克服这一点。看起来它可以用更简单的机制完成这项工作。

API健康检查工具-C/C++库的测试框架:

共享C/C++库基本单元测试的自动生成器。它能够通过分析头文件中的声明为API中的每个函数生成合理(在大多数情况下,但不幸的是不是所有情况下)的参数输入数据并编写简单(“健全”或“浅层”质量)的测试用例。

生成测试的质量允许检查简单用例中没有关键错误。该工具能够构建和执行生成的测试,并检测崩溃(段故障)、中止、各种发出的信号、非零程序返回代码和程序挂起。

示例:

在阅读了MinUnit之后,我认为更好的方法是将测试建立在断言宏中,我使用了很多像防御程序技术一样的方法。所以我使用了与标准断言混合的MinUnit相同的想法。你可以在k0ga的博客中看到我的框架(一个好名字可以是NoMinUnit)

cpputest-强烈推荐用于单元测试C代码的框架。

本线程嵌入式C的TDD中提到的书中的示例是使用CppUTest编写的。

作为一名C新手,我发现名为C语言中的测试驱动开发的幻灯片非常有用。基本上,它使用标准assert()&&来传递消息,没有任何外部依赖关系。如果有人习惯了全栈测试框架,这可能不行:)

我说的几乎和RATKKO一样,但是如果你对单元测试有一个嵌入的扭曲,那么…

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的TDD中提到的书中的示例是使用Unity(和CppUTest)编写的。

有一个优雅的C单元测试框架,支持称为cmocka的模拟对象。它只需要标准C库,适用于一系列计算平台(包括嵌入式)和不同的编译器。

它还支持不同的消息输出格式,如子单元、测试任何协议和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月发布。

我很惊讶没有人提到Cutter(http://cutter.sourceforge.net/)您可以测试C和C++,它与自动工具无缝集成,并提供非常好的教程。

我们编写了作弊(托管在github上)以便于可用性和可移植性。

它没有依赖关系,不需要安装或配置。只需要一个头文件和一个测试用例。

#include <cheat.h>
CHEAT_TEST(mathematics_still_work,cheat_assert(2 + 2 == 4);cheat_assert_not(2 + 2 == 5);)

测试编译成一个可执行文件,负责运行测试并报告其结果。

$ gcc -I . tests.c$ ./a.out..---2 successful of 2 runSUCCESS

它也有漂亮的颜色。

我只是出于对现有C单元测试库的失望而编写了LibCut。它具有原语的自动类型串接(不需要test_eq_int,test_eq_long,test_eq_short等…;只有两个不同的原语和字符串集)并且由一个头文件组成。这是一个简短的示例:

#include <libcut.h>
LIBCUT_TEST(test_abc) {LIBCUT_TEST_EQ(1, 1);LIBCUT_TEST_NE(1, 0);LIBCUT_TEST_STREQ("abc", "abc");LIBCUT_TEST_STRNE("abc", "def");}
LIBCUT_MAIN(test_abc);

它只适用于C11。