it-swarm.cn

单元测试C代码

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

有没有办法让单元测试普通C代码就像单元测试Java代码一样简单,例如 JUnit ?任何专门针对嵌入式开发(交叉编译到arm-linux平台)的见解都将非常感激。

805
Paul Osborne

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

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

一组最小的宏,就是这样!关键是要表明对代码进行单元测试是多么容易。请参阅 MinUnit主页

安藤先生的CUnit

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

此列表最后更新于2008年3月。 /

更多框架:

CMocka

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

CMocka主页

标准

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

有关详细信息,请参阅 Criterion主页

HWUT

HWUT是一个通用的单元测试工具,对C有很好的支持。它可以帮助创建Makefile,生成在最小“迭代表”中编码的大量测试用例,沿着状态机走,生成C-stub等等。一般方法非常独特:Verdicts基于'良好的stdout/bad stdout'。但是,比较功能是灵活的。因此,可以使用任何类型的脚本进行检查。它可以应用于任何可以产生标准输出的语言。

HWUT主页

CGreen

适用于C和C++的现代,可移植,跨语言的单元测试和模拟框架。它提供了一个可选的BDD表示法,一个模拟库,能够在一个进程中运行它(使调试更容易)。可以自动发现测试功能的测试运行器。但您可以通过编程方式创建自己的。

所有这些功能(以及更多)都在 CGreen手册 中进行了解释。

维基百科提供了 单元测试框架列表下C单元测试框架的详细列表:C

461
Adam Rosenfield

我个人喜欢 Google Test framework

测试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中并给出一个空实现。这意味着测试文件独立于代码库的其余部分进行编译和链接并单独执行。

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

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

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

编辑 :我写了一篇关于单元测试程序代码的 博客文章 ,其中 源代码在GitHub上可用

编辑 :有一个 来自实用程序员的新书 ,它专门针对单元测试C代码 我强烈推荐

152
mikelong

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

128
Matteo Caprari

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

http://cutest.sourceforge.net/

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

  • 在您调用CuTest例程的任何位置都包含头文件
  • 要编译/链接到图像中的单个附加“C”文件
  • 一些简单的代码添加到main来设置和调用单元测试 - 我只是在一个特殊的main()函数中,如果在构建过程中定义了UNITTEST,它就会被编译。

系统需要支持堆和一些stdio功能(并非所有嵌入式系统都具有)。但是代码很简单,如果您的平台没有这些代码,您可能可以替代这些需求。

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

40
Michael Burr

我说和ratkok几乎一样但是如果你对单元测试有一个嵌入式扭曲那么......

Unity - 强烈建议的单元测试C代码框架。

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

36
Johan

您还可以查看 libtap ,这是一个输出测试任何协议(TAP)的C测试框架,因此可以很好地集成到该技术的各种工具中。它主要用于动态语言世界,但它易于使用并且变得非常流行。

一个例子:

#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();
}
32
Ovid

有一个优雅的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月发布。

26
asn

在开始寻找模拟函数的方法之前,我没有对遗留的C应用程序进行远程测试。我非常需要模拟来隔离我想要测试的C文件。我尝试了一下cmock,我想我会采用它。

Cmock扫描头文件并根据它找到的原型生成模拟函数。模拟将允许您完美隔离测试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需要更多地了解模拟才能开始。示例可以帮助您克服这个问题。看起来它可以用更简单的机制来完成工作。

20
Philippe A.

作为C新手,我发现幻灯片名为 测试驱动开发在C 非常有用。基本上,它使用标准assert()&&来传递消息,而不需要任何外部依赖。如果有人习惯了完整的堆栈测试框架,这可能不会做:)

15
chelmertz

我不使用框架,我只是使用autotools“检查”目标支持。实现“main”并使用assert(s)。

我的测试目录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
12
navicore

CUnit

嵌入式单元 是嵌入式C系统的单元测试框架。它的设计是从JUnit和CUnit等复制而来,然后在某种程度上适用于嵌入式C系统。嵌入式单元不需要std C libs。所有对象都分配给const区域。

Tessy 自动化嵌入式软件的单元测试。

12
prakash

我们写了 _作弊_ (托管在 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 run
SUCCESS

它也有漂亮的颜色。

12
Tuplanolla

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

有一些与C相关的依赖注入相关的技术,我还没有在其他任何地方看到过。

11
Frank Schwieterman

CppUTest - 强烈建议的单元测试C代码框架。

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

7
ratkok

除了我明显的偏见

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

是一个很简单的单元测试C代码的方法。模仿xUnit

6
Keith Nicholas

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

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

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

CxxTest用户指南CxxTest Wiki

6
Zing-

读完Minunit之后,我认为更好的方法是在断言宏中进行测试,我使用的很像防御程序技术。所以我使用了Minunit与标准断言相同的想法。您可以在 k0ga的博客中看到我的框架(一个好名字可能是NoMinunit)

5
Roberto Vargas Caballero

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

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

4
Paweł Hajdan
4
Landon Kuhn

Cmockery 是最近推出的一个项目,它包含一个非常简单易用的C库,用于编写单元测试。

4
Alejandro Bologna

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

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

3
Kevin
2
Tony Bai

API Sanity Checker - C/C++库的测试框架:

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

生成的测试质量允许在简单的用例中检查是否存在严重错误。该工具能够构建和执行生成的测试并检测崩溃(段错误),中止,各种发出的信号,非零程序返回码和程序挂起。

例子:

2
linuxbuild

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

2
Gerhard

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

假设你有c ++编译器来进行单元测试。如果没有,那么我必须同意亚当罗森菲尔德,检查是你想要的。

2
Kwondri

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

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

1
bongo

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

1
Kris

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

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

1
quamrana

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

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

0
Dushara

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

0
Johannes Passing

我刚写了 Libcut 对现有的C单元测试库感到沮丧。它具有基元的自动类型字符串(不需要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。

0
kirbyfan64sos