开始使用CppUTest

开始使用CppUTest

开始使用CppUTest

May 14, 2017

出版商

出版商

Bird

Bird

-

类别

类别

电子邮件

电子邮件

Ready to see Bird
in action?

Ready to see Bird
in action?

Getting Started with CppUTest

在SparkPost,我们投入了大量的时间和精力来测试我们的代码。我们的平台是用C语言编写的,最近我研究了与一个名为 "CppUTest "的单元测试框架的整合,它为C/C++提供xUnit风格的测试。这个框架很强大,功能丰富,而且正在积极开发中,这使它成为一个很好的选择。它还提供了一个C语言集成层,这使得它很容易与我们的平台C代码一起使用,尽管该框架的大部分是C++。本教程包括如何在自己的项目中开始使用CppUTest。


下载CppUTest

ǞǞǞ CppUTest project page is available here, and the repository is on github. It’s also included in the package management repositories for many linux distros, as well as 自酿葡萄酒 on Mac OS. ǞǞǞ examples that follow were executed on Mac OS X, but they’re derived from code written for Red Hat, the OS our platform runs on.

The basics are well documented on CppUTest’s 主页. We’re going to breeze through that and get to some of the more interesting features.


奠定基础

首先,让我们写一些代码吧

我们的测试项目将有一个 "main "文件,并将包括一个名为 "code "的实用程序库。该库将提供一个简单的函数,返回1(目前)。这些文件将被布置成这样。

├── src │ ├── code │ ├── code.cpp │ └── code.h │ └── main.cpp └── t └── main.cpp └── test.cpp

让我们从编写src/文件开始

// src/main.cpp #include <stdlib.h> #include <stdio.h> #include "code.h" int main(void) { test_func(); printf("hello world!\n"); exit(0); }

// src/code/code.cpp #include <stdlib.h> #include "code.h" int test_func () { return 1; }

// src/code/code.h #ifndef __code_h__ #define __code_h__ int test_func (); #endif

Now, let’s do the tests, which will live in the t/ directory.  The first thing to do is to set up a test runner which will run our test files. This is also the ‘main’  function that will execute once this is all compiled:

// t/main.cpp #include "CppUTest/CommandLineTestRunner.h" int main(int ac, char** av) { return CommandLineTestRunner::RunAllTests(ac, av); }

现在我们可以编写我们的第一个测试模块。

// t/test.cpp #include "CppUTest/TestHarness.h" #include "code.h" TEST_GROUP(AwesomeExamples) { }; TEST(AwesomeExamples, FirstExample) { int x = test_func(); CHECK_EQUAL(1, x); }

Next, we need to write makefiles.  We’ll need two: one for the project files under src/, and one for the tests.


项目Makefile

项目的makefile将与项目根部的'src'和't'目录处于同一级别。它应该看起来像这样。

# Makefile SRC_DIR=./src CODE_DIR=$(SRC_DIR)/code OUT=example TEST_DIR=t test: make -C $(TEST_DIR) test_clean: make -C $(TEST_DIR) clean code.o: gcc -c -I$(CODE_DIR) $(CODE_DIR) /code。cpp -o $(CODE_DIR)/code.o main: code.o gcc -I$(CODE_DIR) $(CODE_DIR)/code.o $(SRC_DIR)/main.cpp -o $(OUT) all: test main clean: test_clean rm $(SRC_DIR) /*.o $(CODE_DIR) /*.o $(OUT)

Note that this uses ‘make -C’  for the test targets – meaning that it will call ‘make’  again using the makefile in the test directory.

在这一点上,我们可以用makefile编译'src'代码,看看它是否正常。

[]$ make main gcc -c -I./src/code ./src/code/code.cpp -o ./src/code/code.o gcc -I./src/code ./src/code/code.o ./src/main.cpp -o example []$ ./example hello world!


测试 Makefile

对于测试来说,事情就有点复杂了,因为我们需要正确地加载和整合CppUTest库。

CppUTest资源库提供了一个名为 "MakefileWorker.mk "的文件。它提供了很多功能,使得用CppUTest构建变得简单。该文件位于git资源库的 "build "目录下。在本教程中,我们将假设它已经被复制到't/'目录中。它可以按以下方式使用。

# 我们不希望使用相对路径。所以我们设置这些变量 PROJECT_DIR=/path/to/project SRC_DIR=$(PROJECT_DIR)/src TEST_DIR=$(PROJECT_DIR)/t # 指定源代码和包括的位置 INCLUDE_DIRS=$(SRC_DIR)/code SRC_DIRS=$(SRC_DIR)/code# 指定测试代码的位置 TEST_SRC_DIRS = $(TEST_DIR) # 测试二进制的调用 TEST_TARGET=example # cpputest库的位置 CPPUTEST_HOME=/usr/local # 运行 MakefileWorker.mk,并使用这里定义的变量 include MakefileWorker.mk

注意,CPPUTEST_HOME必须被设置为CppUTest的安装位置。如果你安装了一个发行版软件包,这通常是在linux/mac系统的/usr/local下。如果你自己检查了repo,那就是检查的地方。

这些选项都在MakefileWorker.mk中有所记载。

MakefileWorker.mk还增加了一些makefile目标,包括以下内容。

  1. all - 构建由makefile指示的测试。

  2. clean - 删除所有为测试生成的对象和gcov文件。

  3. realclean - 删除整个目录树中的任何对象或gcov文件

  4. flags - 列出用于编译测试的所有配置的标志。

  5. debug - 列出所有的源文件、对象、依赖关系和 "需要清理的东西"。


代码覆盖率

Unit testing would not be complete without a coverage report. The go-to tool for this for projects using gcc is gcov, available as part of the standard suite of gcc utilities. Cpputest integrates easily with gcov, all you need to do is add this line 到 makefile:

CPPUTEST_USE_GCOV=Y

Next, we need to make sure that the filterGcov.sh script from 这个 repo is in ‘/scripts/filterGcov.sh’ relative to wherever you set ‘CPPUTEST_HOME’ to be. It also needs to have execute perms.

在示例的Makefile中,它将被部署到'/usr/local/scripts/filterGcov.sh'。如果你从 repo checkout 中运行 CppUTest,一切都应该不需要修改。


有了这些,你可以简单地运行 "make gcov",分析就会为你生成。在我们的例子中,我们需要'make -B'来重建启用了gcov的对象文件。

[]$ make -B gcov < compilation output > for d in /Users/ykuperman/code/blogpost/qa/src/code ; do \ FILES=`ls $d/*.c $d/*.cc $d/*.cpp 2> /dev/null` ; \ gcov --object-directory objs/$d $FILES >> gcov_output.txt 2>>gcov_error.txt ; \ done for f in ; do \ gcov --object-directory objs/$f $f >> gcov_output.txt 2>>gcov_error.txt ; \ done /usr/local/scripts/filterGcov.sh gcov_output.txt gcov_error.txt gcov_report.txt example.txt cat gcov_report.txt 100.00% /Users/ykuperman/code/blogpost/qa/src/code/code.cpp mkdir -p gcov mv *.gcov gcov mv gcov_* gcov See gcov directory for details

这将输出一些文件到一个新的'gcov'目录。这些文件是

  1. code.cpp.gcov - 被测试代码的实际'gcov'文件

  2. gcov_error.txt - 错误报告(在我们的例子中,它应该是空的)。

  3. gcov_output.txt - 运行gcov命令的实际输出。

  4. gcov_report.txt - 测试中每个文件的覆盖率摘要

  5. gcov_report.txt.html - gcov_report的html版本


Cpputest内存泄漏检测

Cpputest允许你通过重新定义标准的 "malloc/free "系列函数来自动检测泄漏的内存,并使用它自己的包装器。这使得它能够快速捕捉泄漏,并在每次测试执行时报告它们。这在MakefileWorker.mk中是默认启用的,所以到目前为止,它已经开始使用所概述的步骤了。

To illustrate, let’s leak some memory in test_func() !

Going back to code.c, we add a malloc()  到 function, like so:

int test_func() { malloc(1); return 1; }

现在,重新编译后,产生了以下错误。

test.cpp:9: error: Failure in TEST(AwesomeExamples, FirstExample) Memory leak(s) found. Alloc num (4) Leak size: 1 Allocated at: ./code.c and line: 6. Type: "malloc" Memory: <0x7fc9e94056d0> Content: 0000: 00 |.| Total number of leaks: 1

这显示了哪个测试导致了泄漏,泄漏发生在源代码的什么地方,以及泄漏的内存是什么。非常有帮助!

这个功能有几个注意事项。

  1. Cpputest使用预处理器宏来动态地重新定义对标准内存管理函数的所有调用。这意味着它只对被测试的源代码中的调用起作用,因为那是用CppUTest的重写编译的。链接库中的泄漏将不会被捕获。

  2. 有时,为整个进程分配的内存并不意味着要被释放。如果你正在测试一个有这种行为的模块,这可能会产生很多垃圾错误。要禁用泄漏检测,你可以这样做。

CPPUTEST_USE_MEM_LEAK_DETECTION=N


对更多内容感兴趣?

说到这个工具中包含的所有功能,这只是冰山一角。除了这里讨论的基本功能外,它还有一个嘲弄框架、一个直接的C语言集成层和一个插件框架,仅举几个重要的例子。Repo还包含了一个完整的辅助脚本目录,这些脚本可以帮助自动处理框架工作中的一些常规部分。

我希望这里的信息能帮助你用这个伟大的工具来提高你的C/C++代码质量!

Your new standard in Marketing, Pay & Sales. It's Bird

The right message -> to the right person ->right time.

By clicking "See Bird" you agree to Bird's 隐私声明.

Your new standard in Marketing, Pay & Sales. It's Bird

The right message -> to the right person ->right time.

By clicking "See Bird" you agree to Bird's 隐私声明.