单元测试C代码
我今年夏天用C语言编写了一个嵌入式系统。这是我工作的公司已经接管的一个现有项目。我已经非常习惯于使用JUnit用Java编写单元测试,但是对于为现有代码(需要重构)以及添加到系统中的新代码编写单元测试的最佳方法感到困惑。
有什么方法可以像使用JUnit这样简单地对普通C代码进行单元测试,就像对Java代码进行单元测试一样容易?非常感谢任何专门用于嵌入式开发(对arm-linux平台进行交叉编译)的见解。
解决方案
回答
C语言中的一个单元测试框架是Check;可以在这里找到C语言中的单元测试框架列表,并在下面复制。根据运行时具有多少标准库功能,我们是否可以使用其中之一。
AceUnit AceUnit (Advanced C and Embedded Unit) bills itself as a comfortable C code unit test framework. It tries to mimick JUnit 4.x and includes reflection-like capabilities. AceUnit can be used in resource constraint environments, e.g. embedded software development, and importantly it runs fine in environments where you cannot include a single standard header file and cannot invoke a single standard C function from the ANSI / ISO C libraries. It also has a Windows port. It does not use forks to trap signals, although the authors have expressed interest in adding such a feature. See the AceUnit homepage. GNU Autounit Much along the same lines as Check, including forking to run unit tests in a separate address space (in fact, the original author of Check borrowed the idea from GNU Autounit). GNU Autounit uses GLib extensively, which means that linking and such need special options, but this may not be a big problem to you, especially if you are already using GTK or GLib. See the GNU Autounit homepage. cUnit Also uses GLib, but does not fork to protect the address space of unit tests. CUnit Standard C, with plans for a Win32 GUI implementation. Does not currently fork or otherwise protect the address space of unit tests. In early development. See the CUnit homepage. CuTest A simple framework with just one .c and one .h file that you drop into your source tree. See the CuTest homepage. CppUnit The premier unit testing framework for C++; you can also use it to test C code. It is stable, actively developed, and has a GUI interface. The primary reasons not to use CppUnit for C are first that it is quite big, and second you have to write your tests in C++, which means you need a C++ compiler. If these don’t sound like concerns, it is definitely worth considering, along with other C++ unit testing frameworks. See the CppUnit homepage. embUnit embUnit (Embedded Unit) is another unit test framework for embedded systems. This one appears to be superseded by AceUnit. Embedded Unit homepage. MinUnit A minimal set of macros and that’s it! The point is to show how easy it is to unit test your code. See the MinUnit homepage. CUnit for Mr. Ando A CUnit implementation that is fairly new, and apparently still in early development. See the CUnit for Mr. Ando homepage. This list was last updated in March 2008.
其他:
卡莫卡
CMocka是C的测试框架,具有对模拟对象的支持。易于使用和设置。 CMocka官方主页。
标准
Criterion是跨平台的C单元测试框架,支持自动测试注册,参数化测试,理论,并且可以输出为多种格式,包括TAP和JUnit XML。每个测试都在其自己的过程中运行,因此可以根据需要报告或者测试信号和崩溃。有关更多信息,请参见Criterion主页。
棚屋
HWUT是具有C的强大支持的通用单元测试工具。它可以帮助创建Makefile,生成用最小的"迭代表"编码的大量测试用例,遍历状态机,生成C存根等等。一般方法非常独特:裁决基于"好的标准输出/不好的标准输出"。但是,比较功能很灵活。因此,任何类型的脚本都可以用于检查。它可以应用于可以产生标准输出的任何语言。请参阅HWUT主页。
Wikipedia在"单元测试框架列表:C"下给出了C单元测试框架的详细列表。
回答
Google具有出色的测试框架。 https://github.com/google/googletest/blob/master/googletest/docs/primer.md
是的,据我所知它将与纯C一起使用,即不需要C ++功能(不确定是否需要C ++编译器)。
回答
位于http://code.google.com/p/cmockery/的cmockery
回答
如果我们熟悉JUnit,则建议使用CppUnit。
http://cppunit.sourceforge.net/cppunit-wiki
那是假设我们有c ++编译器来进行单元测试。如果不是,那么我必须同意亚当·罗森菲尔德的观点,那就是我们要检查。
回答
一种使用的技术是使用C ++ xUnit框架(和C ++编译器)开发单元测试代码,同时将目标系统的源代码保持为C模块。
确保我们定期在交叉编译器下编译C源代码,并在可能的情况下自动进行单元测试。
回答
有CUnit
嵌入式单元是嵌入式C系统的单元测试框架。它的设计是从JUnit和CUnit等复制而来的,然后在某种程度上适用于Embedded C System。嵌入式单元不需要std C库。所有对象都分配给const区域。
Tessy使嵌入式软件的单元测试自动化。
回答
Minunit是一个非常简单的单元测试框架。
我正在用它来对AVR的C微控制器代码进行单元测试。
回答
首先,请看这里: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用户指南
CxxTest Wiki
回答
我们可能还想看看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看起来像:
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
回答
Cmockery是一个最近启动的项目,包含一个非常简单易用的C库来编写单元测试。
回答
我目前正在使用CuTest单元测试框架:
http://cutest.sourceforge.net/
它非常轻巧和简单,因此非常适合嵌入式系统。使它在目标平台和台式机上都能正常工作是没有问题的。除了编写单元测试之外,还需要:
- 在调用CuTest例程的任何地方都包含一个头文件
- 单个添加的" C"文件将被编译/链接到映像中
- 在main中添加了一些简单的代码来设置和调用单元测试-我只是将其放在特殊的main()函数中,如果在构建过程中定义了UNITTEST,则该函数会进行编译。
系统需要支持堆和某些stdio功能(并非所有嵌入式系统都具有)。但是代码很简单,如果平台没有这些需求,我们可能可以替代这些需求。
明智地使用extern" C" {}块,它也支持测试C ++。
回答
如果我们以Win32平台或者NT内核模式为目标,则应查看cfix。
回答
在对目标进行测试之前,我使用RCUNIT对PC上的嵌入式代码进行了一些单元测试。良好的硬件接口抽象很重要,否则字节序和内存映射寄存器将使我们丧命。
回答
如果我们仍在寻找测试框架,则CUnitWin32是Win32 / NT平台的一种。
这解决了我在其他测试框架中遇到的一个基本问题。也就是说,全局/静态变量处于确定性状态,因为每个测试都作为单独的过程执行。
回答
我个人喜欢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中并提供了一个空的实现。这意味着测试文件独立于其余代码库进行编译和链接,并独立执行。
至于交叉编译代码,要使其正常工作,我们需要在目标设备上提供良好的工具。我是通过将googletest cross编译为PowerPC架构上的Linux来实现的。这是有道理的,因为那里有一个完整的shell和os可以收集结果。对于不太丰富的环境(我将其归类为没有完整操作系统的所有内容),我们应该仅在主机上构建并运行。无论如何,我们都应该这样做,以便我们可以在构建过程中自动运行测试。
我发现测试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代码进行单元测试的一种很好的简单方法。模仿xUnit
回答
在开始寻找一种模拟函数的方法之前,我并没有对遗留的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需要对模拟有更多的了解才能上手。示例可以克服这一问题。看起来它可以用更简单的机制完成任务。
回答
尝试lcut! http://code.google.com/p/lcut