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

有什么方法可以使单元测试纯 C 代码像单元测试 Java 代码一样简单,例如: 联合单元?任何专门适用于嵌入式开发(交叉编译到arm-linux平台)的见解将不胜感激。

有帮助吗?

解决方案

C 语言的一个单元测试框架是 查看;可以找到 C 语言单元测试框架的列表 这里 并转载如下。根据您的运行时有多少个标准库函数,您可能能够或不能使用其中之一。

王牌单位

AceUnit(高级 C 和嵌入式单元)将自己定位为舒适的 C 代码单元测试框架。它尝试模仿 JUnit 4.x 并包含类似反射的功能。AceUnit 可用于资源有限的环境,例如嵌入式软件开发,重要的是,它在无法包含单个标准头文件且无法从 ANSI / ISO C 库调用单个标准 C 函数的环境中运行良好。它还具有 Windows 端口。它不使用分叉来捕获信号,尽管作者表示有兴趣添加这样的功能。请参阅 AceUnit 主页.

GNU 自动单元

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

单位

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

单位

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

铜测试

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

Cpp单元

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

嵌入单位

embUnit(Embedded Unit)是另一个针对嵌入式系统的单元测试框架。这个似乎已被 AceUnit 取代。 嵌入式单元主页.

最小单位

一组最小的宏,就是这样!重点是展示对代码进行单元测试是多么容易。请参阅 最小单位主页.

先生的CUnit安藤

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

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

更多框架:

莫卡

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

CMocka 主页.

标准

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

请参阅 标准主页 了解更多信息。

哈尔滨工业大学

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

HWUT主页.

绿色

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

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

我发现测试 C++ 代码通常要容易得多,因为 OO 代码通常比过程代码耦合性要低得多(当然这在很大程度上取决于编码风格)。同样在 C++ 中,您可以使用依赖注入和方法重写等技巧来将接缝插入到原本封装的代码中。

迈克尔·费瑟斯有一个 关于测试遗留代码的优秀书籍. 。在其中一章中,他介绍了我强烈推荐的处理非面向对象代码的技术。

编辑:我写了一个 博客文章 关于单元测试程序代码,与 GitHub 上提供源代码.

编辑:有一个 务实程序员出版的新书 专门针对单元测试 C 代码 我强烈推荐.

小单位 是一个非常简单的单元测试框架。我用它来对 avr 的 c 微控制器代码进行单元测试。

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

http://cutest.sourceforge.net/

它非常适合嵌入式系统,因为它非常轻量且简单。我可以毫无问题地让它在目标平台和桌面上运行。除了编写单元测试之外,还需要:

  • 标题文件包括您在哪里调用最可爱的例程
  • 要编译/链接到图像的一个附加的“ C”文件
  • 一些简单的代码添加到MAIN中以设置并调用单元测试 - 如果在构建过程中定义了UNITSEST,则将其放在特殊的Main()函数中。

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

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

我说的几乎与ratkok相同,但如果你对单元测试有嵌入的扭曲,那么......

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

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

您可能还想看看 libtap, ,一个 C 测试框架,它输出 Test Anything Protocol (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();
}

有一个优雅的 C 单元测试框架,支持模拟对象,称为 克莫卡. 。它只需要标准 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);
}

应用程序编程接口 有完整的文档记录,并且几个示例是源代码的一部分。

要开始使用 cmocka,您应该阅读 LWN.net 上的文章: 使用 C 中的模拟对象进行单元测试

cmocka 1.0 已于 2015 年 2 月发布。

在我开始寻找模拟函数的方法之前,我并没有对遗留的 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 需要对模拟有更多的了解才能开始。示例应该可以帮助您克服这个问题。看起来它可以用更简单的机制来完成这项工作。

作为一个 C 新手,我发现了名为 C 语言测试驱动开发 很有帮助。基本上,它使用标准 assert() 和...一起 && 传递消息,无需任何外部依赖。如果有人习惯了全栈测试框架,这可能行不通:)

单位

嵌入式单元 是嵌入式 C 系统的单元测试框架。它的设计复制自 JUnit 和 CUnit 等,然后针对嵌入式 C 系统进行了一些修改。嵌入式单元不需要 std C 库。所有对象都分配到 const 区域。

泰西 自动执行嵌入式软件的单元测试。

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

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

我们写 欺骗 (主办于 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

它也有漂亮的颜色。

Michael Feather 的书《有效处理遗留代码》介绍了许多 C 开发期间特定于单元测试的技术。

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

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

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

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

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

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

CxxTest 用户指南 维基百科

除了我明显的偏见

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

是一种对 C 代码进行单元测试的简单好方法。模仿xUnit

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

谷歌拥有优秀的测试框架。 https://github.com/google/googletest/blob/master/googletest/docs/primer.md

是的,据我所知,它可以与纯 C 一起使用,即不需要 C++ 功能(可能需要 C++ 编译器,不确定)。

嘲讽 是一个最近启动的项目,由一个非常简单易用的 C 库组成,用于编写单元测试。

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

我公司有一个供客户使用的 C 库。我们使用CxxTest(一个C++单元测试库)来测试代码。CppUnit 也可以工作。如果您陷入 C 困境,我推荐 RCUNIT(但 CUnit 也很好)。

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

假设您有 C++ 编译器来执行单元测试。如果不是,那么我必须同意亚当·罗森菲尔德的观点,支票就是你想要的。

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

API 完整性检查器 — C/C++ 库的测试框架:

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

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

例子:

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

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

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

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

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

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

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

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top