今年夏天,我开发了一个用纯c语言编写的嵌入式系统。这是我所在公司接管的一个现有项目。我已经非常习惯使用JUnit在Java中编写单元测试,但不知道为现有代码(需要重构)和添加到系统中的新代码编写单元测试的最佳方法是什么。
有什么项目可以让单元测试纯C代码像使用JUnit测试Java代码一样简单吗?任何特别适用于嵌入式开发(交叉编译到arm-linux平台)的见解都将非常感谢。
今年夏天,我开发了一个用纯c语言编写的嵌入式系统。这是我所在公司接管的一个现有项目。我已经非常习惯使用JUnit在Java中编写单元测试,但不知道为现有代码(需要重构)和添加到系统中的新代码编写单元测试的最佳方法是什么。
有什么项目可以让单元测试纯C代码像使用JUnit测试Java代码一样简单吗?任何特别适用于嵌入式开发(交叉编译到arm-linux平台)的见解都将非常感谢。
当前回答
我不使用框架,我只是使用自动工具“检查”目标支持。实现一个“main”并使用assert。
我的测试目录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
其他回答
在测试目标之前,我使用RCUNIT在PC上对嵌入式代码进行了一些单元测试。好的硬件接口抽象很重要,否则字节顺序和内存映射寄存器会害死你。
Minunit是一个非常简单的单元测试框架。 我用它来单元测试c微控制器代码avr。
谷歌拥有优秀的测试框架。https://github.com/google/googletest/blob/master/googletest/docs/primer.md
是的,据我所知,它将与普通C一起工作,即不需要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代码的单元测试,我强烈推荐这本书。
我目前正在使用CuTest单元测试框架:
http://cutest.sourceforge.net/
它是嵌入式系统的理想选择,因为它非常轻量级和简单。让它在目标平台和桌面上运行都没有问题。除了编写单元测试,所需要的是:
头文件包含在任何地方 你称之为最可爱的程序 一个额外的“C”文件 编译/链接到映像 一些简单的代码添加到主要到 设置并调用单元测试- I 只需要把它放在一个特殊的main()中 函数,如果 过程中定义UNITTEST 构建。
系统需要支持堆和一些stdio功能(不是所有的嵌入式系统都有)。但是代码非常简单,如果您的平台没有这些需求,您可能可以使用这些需求的替代方案。
通过明智地使用extern“C”{}块,它还可以很好地支持测试c++。