今年夏天,我开发了一个用纯c语言编写的嵌入式系统。这是我所在公司接管的一个现有项目。我已经非常习惯使用JUnit在Java中编写单元测试,但不知道为现有代码(需要重构)和添加到系统中的新代码编写单元测试的最佳方法是什么。
有什么项目可以让单元测试纯C代码像使用JUnit测试Java代码一样简单吗?任何特别适用于嵌入式开发(交叉编译到arm-linux平台)的见解都将非常感谢。
今年夏天,我开发了一个用纯c语言编写的嵌入式系统。这是我所在公司接管的一个现有项目。我已经非常习惯使用JUnit在Java中编写单元测试,但不知道为现有代码(需要重构)和添加到系统中的新代码编写单元测试的最佳方法是什么。
有什么项目可以让单元测试纯C代码像使用JUnit测试Java代码一样简单吗?任何特别适用于嵌入式开发(交叉编译到arm-linux平台)的见解都将非常感谢。
当前回答
我说几乎和ratkok一样,但如果你有一个嵌入的扭曲单元测试,那么……
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语言中的一个单元测试框架是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
CMocka是一个支持模拟对象的C语言测试框架。它易于使用和设置。
请参阅CMocka主页。
标准
Criterion是一个跨平台的C单元测试框架,支持自动测试注册、参数化测试和理论,可以输出多种格式,包括TAP和JUnit XML。每个测试都在自己的进程中运行,因此如果需要,可以报告或测试信号和崩溃。
更多信息请参阅Criterion主页。
HWUT
HWUT是一个通用的单元测试工具,支持c语言。它可以帮助创建makefile,生成大量的测试用例,在最小的“迭代表”中编码,运行状态机,生成c语言存根等等。一般的方法是非常独特的:裁决是基于“好标准输出/坏标准输出”。不过,比较函数是灵活的。因此,任何类型的脚本都可以用于检查。它可以应用于任何可以产生标准输出的语言。
请参阅武汉理工大学主页。
CGreen
一个现代的、可移植的、跨语言的C和c++单元测试和模拟框架。它提供了可选的BDD表示法、模拟库和在单个进程中运行它的能力(使调试更容易)。一个自动发现测试函数的测试运行器是可用的。但是您可以通过编程方式创建自己的。
所有这些特性(以及更多)在CGreen手册中都有解释。
维基百科在单元测试框架列表下给出了C单元测试框架的详细列表:C
我个人喜欢谷歌测试框架。
测试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有一本关于测试遗留代码的优秀书籍。在其中一章中,他介绍了处理非oo代码的技巧,我强烈推荐这些技巧。
编辑:我写了一篇关于单元测试过程代码的博客文章,源代码可以在GitHub上找到。
编辑:Pragmatic Programmers出版了一本新书,专门介绍了C代码的单元测试,我强烈推荐这本书。
我编写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有效。
谷歌拥有优秀的测试框架。https://github.com/google/googletest/blob/master/googletest/docs/primer.md
是的,据我所知,它将与普通C一起工作,即不需要c++功能(可能需要c++编译器,不确定)。
在开始寻找模拟函数的方法之前,我还没有深入测试遗留的C应用程序。我非常需要mock来将我想测试的C文件与其他C文件隔离开来。我试过cmock,我想我会采用它。
Cmock扫描头文件,并根据它找到的原型生成模拟函数。mock将允许您在完全隔离的情况下测试C文件。您所要做的就是将您的测试文件链接到模拟对象,而不是真正的对象文件。
cmock的另一个优点是,它将验证传递给模拟函数的参数,并允许您指定模拟函数应该提供的返回值。这对于在函数中测试不同的执行流非常有用。
测试由典型的testA()、testB()函数组成,您可以在其中构建期望、调用函数来测试和检查断言。
最后一步是为unity的测试生成一个运行器。Cmock绑定到统一测试框架。Unity和其他单元测试框架一样容易学习。
很值得一试,而且很容易掌握:
http://sourceforge.net/apps/trac/cmock/wiki
更新1
我正在研究的另一个框架是cmock。
http://code.google.com/p/cmockery/
它是一个支持单元测试和模拟的纯C框架。它不依赖于ruby(与Cmock相反),也很少依赖于外部库。
它需要更多的手工工作来设置模拟,因为它不生成代码。这并不代表现有项目的大量工作,因为原型不会有太大的变化:一旦有了mock,就暂时不需要更改它们(这是我的例子)。额外的类型提供了对模拟的完全控制。如果有什么你不喜欢的,你只需改变你的mock。
不需要特别的测试人员。您只需要创建一个测试数组,并将其传递给run_tests函数。这里也有更多的手工工作,但我绝对喜欢自包含自治框架的想法。
此外,它还包含了一些我不知道的漂亮的C技巧。
总的来说,cmock需要对mock有更多的理解才能开始。例子可以帮助你克服这个问题。看起来它可以用更简单的机制来完成这项工作。