CPPUTest 单元测试框架

简介:
CPPUTest 虽然名称上看起来是 C++ 的 单元测试框架, 其实它也是支持测试 C 代码的.
  本文主要介绍用CPPUTest来测试 C 代码. (C++没用过, 平时主要用的是C) C++相关的内容都省略了.
  本文基于 debian v7.6 x86_64.
   1. CPPUTest 安装
  现在各个 Linux的发行版的源都有丰富的软件资源, 而且安装方便.
  但是如果想要在第一时间使用最新版本的开源软件, 还是得从源码安装.
  debian系统为了追求稳定性, apt源中的软件一般都比较旧. 所以本文中的例子是基于最新源码的CPPUTest.
  1.1 apt-get 安装
  $ sudo apt-get install cpputest
  1.2 源码安装
  1. 下载源码, 官网: http://cpputest.github.io/
  2. 编译源码
  $ tar zxvf cpputest-3.6.tar.gz
  $ cd cpputest-3.6/
  $ ./configure
  $ make
  最后我没有实际安装, 而是直接使用编译出的二进制。
   2. CPPUTest 介绍
  2.1 构造待测试代码 (C语言)
/* file: sample.h */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct Student
{
char* name;
int score;
};
void ret_void(void);
int ret_int(int, int);
double ret_double(double, double);
char* ret_pchar(char*, char*);
struct Student* init_student(struct Student* s, char* name, int score);
/* file: sample.c */
#include "sample.h"
#ifndef CPPUTEST
int main(int argc, char *argv[])
{
char* pa;
char* pb;
pa = (char*) malloc(sizeof(char) * 80);
pb = (char*) malloc(sizeof(char) * 20);
strcpy(pa, "abcdefg\0");
strcpy(pb, "hijklmn\0");
printf ("Sample Start......\n");
ret_void();
printf ("ret_int: %d\n", ret_int(100, 10));
printf ("ret_double: %.2f\n", ret_double(100.0, 10.0));
printf ("ret_pchar: %s\n", ret_pchar(pa, pb));
struct Student* s = (struct Student*) malloc(sizeof(struct Student));
s->name = (char*) malloc(sizeof(char) * 80);
init_student(s, " test cpputest", 100);
printf ("init_Student: name=%s, score=%d\n", s->name, s->score);
printf ("Sample End  ......\n");
free(pa);
free(pb);
free(s->name);
free(s);
return 0;
}
#endif
void ret_void()
{
printf ("Hello CPPUTest!\n");
}
/* ia + ib */
int ret_int(int ia, int ib)
{
return ia + ib;
}
/* da / db */
double ret_double(double da, double db)
{
return da / db;
}
/* pa = pa + pb */
char* ret_pchar(char* pa, char* pb)
{
return strcat(pa, pb);
}
/* s->name = name, s->score = score */
void init_student(struct Student* s, char* name, int score)
{
strcpy(s->name, name);
s->score = score;
}
2.2 测试用例的组成, 写法
  CPPUTest 的测试用例非常简单, 首先定义一个 TEST_GROUP, 然后定义属于这个 TEST_GROUP 的 TEST.
  需要注意的地方是:
  1. 引用 CPPUTest 中的2个头文件
  #include <CppUTest/CommandLineTestRunner.h>
  #include <CppUTest/TestHarness.h>
  2. 引用 C 头文件时, 需要使用 extern "C" {}
  extern "C"
  {
  #include "sample.h"
  }
  下面的例子是测试 sample.c 中 ret_int 的代码.
  构造了一个测试成功, 一个测试失败的例子
/* file: test.c */
#include <CppUTest/CommandLineTestRunner.h>
#include <CppUTest/TestHarness.h>
extern "C"
{
#include "sample.h"
}
/* 定义个 TEST_GROUP, 名称为 sample */
TEST_GROUP(sample)
{};
/* 定义一个属于 TEST_GROUP 的 TEST, 名称为 ret_int_success */
TEST(sample, ret_int_success)
{
int sum = ret_int(1, 2);
CHECK_EQUAL(sum, 3);
}
/* 定义一个属于 TEST_GROUP 的 TEST, 名称为 ret_int_failed */
TEST(sample, ret_int_failed)
{
int sum = ret_int(1, 2);
CHECK_EQUAL(sum, 4);
}
int main(int argc, char *argv[])
{
CommandLineTestRunner::RunAllTests(argc, argv);
return 0;
}
   2.3 测试用例结果判断 ( fail, 各种assert等等)
  测试完成后, 可以用 CPPUTest 提供的宏来判断测试结果是否和预期一致.
  CPPUTest 提供的用于判断的宏如下: (上面的测试代码就使用了 CHECK_EQUAL)
   2.4 运行测试用例时的编译选项配置 (主要是C语言相关的)
  这一步是最关键的, 也就是编译出单元测试文件. 下面是 makefile 的写法, 关键位置加了注释.
# makefile for sample cpputest
CPPUTEST_HOME = /home/wangyubin/Downloads/cpputest-3.6
CC      := gcc
CFLAGS    := -g -Wall
CFLAGS  += -std=c99
CFLAGS  += -D CPPUTEST            # 编译测试文件时, 忽略sample.c的main函数, sample.c的代码中用了宏CPPUTEST
# CPPUTest 是C++写的, 所以用 g++ 来编译 测试文件
CPP     := g++
CPPFLAGS  := -g -Wall
CPPFLAGS  += -I$(CPPUTEST_HOME)/include
LDFLAGS := -L$(CPPUTEST_HOME)/lib -lCppUTest
sample: sample.o
sample.o: sample.h sample.c
$(CC) -c -o sample.o sample.c $(CFLAGS)
# 追加的测试程序编译
test: test.o sample.o
$(CPP) -o $@ test.o sample.o $(LDFLAGS)
test.o: sample.h test.c
$(CPP) -c -o test.o test.c $(CPPFLAGS)
.PHONY: clean
clean:
@echo "clean..."
rm -f test sample
rm -f sample.o test.o
  编译测试文件
  make test  <-- 会生成一个文件名为 test 可执行文件
  编译sample程序时, 需要把 "CFLAGS  += -D CPPUTEST" 这句注释掉, 否则没有main函数.
运行可执行文件 test 就可以实施测试.
$ ./test    <-- 默认执行, 没有参数
test.c:34: error: Failure in TEST(sample, ret_int_failed)
expected <3>
but was  <4>
difference starts at position 0 at: <          4         >
^
..
Errors (1 failures, 2 tests, 2 ran, 2 checks, 0 ignored, 0 filtered out, 1 ms)
=================================================================================
$ ./test -c   <-- -c 执行结果加上颜色 (成功绿色, 失败红色)
test.c:34: error: Failure in TEST(sample, ret_int_failed)
expected <3>
but was  <4>
difference starts at position 0 at: <          4         >
^
..
Errors (1 failures, 2 tests, 2 ran, 2 checks, 0 ignored, 0 filtered out, 1 ms) <-- bash中显示红色
=================================================================================
$ ./test -v  <-- -v 显示更为详细的信息
TEST(sample, ret_int_failed)
test.c:34: error: Failure in TEST(sample, ret_int_failed)
expected <3>
but was  <4>
difference starts at position 0 at: <          4         >
^
- 1 ms
TEST(sample, ret_int_success) - 0 ms
Errors (1 failures, 2 tests, 2 ran, 2 checks, 0 ignored, 0 filtered out, 1 ms)
=================================================================================
$ ./test -r 2   <-- -r 指定测试执行的次数, 这里把测试重复执行2遍
Test run 1 of 2
test.c:34: error: Failure in TEST(sample, ret_int_failed)
expected <3>
but was  <4>
difference starts at position 0 at: <          4         >
^
..
Errors (1 failures, 2 tests, 2 ran, 2 checks, 0 ignored, 0 filtered out, 0 ms)
Test run 2 of 2
test.c:34: error: Failure in TEST(sample, ret_int_failed)
expected <3>
but was  <4>
difference starts at position 0 at: <          4         >
^
..
Errors (1 failures, 2 tests, 2 ran, 2 checks, 0 ignored, 0 filtered out, 1 ms)
=================================================================================
$ ./test -g sample    <-- -g 指定 TEST_GROUP, 本例其实只有一个 TEST_GROUP sample
test.c:34: error: Failure in TEST(sample, ret_int_failed)
expected <3>
but was  <4>
difference starts at position 0 at: <          4         >
^
..
Errors (1 failures, 2 tests, 2 ran, 2 checks, 0 ignored, 0 filtered out, 1 ms)
=================================================================================
$ ./test -n ret_int_success    <-- -s 指定执行其中一个 TEST, 名称为 ret_int_success
.
OK (2 tests, 1 ran, 1 checks, 0 ignored, 1 filtered out, 0 ms)
=================================================================================
$ ./test -v -n ret_int_success  <-- 参数也可以搭配使用
TEST(sample, ret_int_success) - 0 ms
OK (2 tests, 1 ran, 1 checks, 0 ignored, 1 filtered out, 0 ms)
   2.6 补充: setup and teardown
  上面 test.c 文件中 TEST_GROUP(sample) 中的代码是空的, 其实 CPPUTest 中内置了 2 个调用 setup 和 teardown.
  在 TEST_GROUP 中实现这2个函数之后, 每个属于这个 TEST_GROUP 的 TEST 在执行之前都会调用 setup, 执行之后会调用 teardown.
  修改 test.c 中的 TEST_GROUP 如下:
/* 定义个 TEST_GROUP, 名称为 sample */
TEST_GROUP(sample)
{
void setup()
{
printf ("测试开始......\n");
}
void teardown()
{
printf ("测试结束......\n");
}
};
  重新执行测试: (每个测试之前, 之后都多了上面的打印信息)
$ make clean
clean...
rm -f test sample
rm -f sample.o test.o
$ make test
g++ -c -o test.o test.c -g -Wall -I/home/wangyubin/Downloads/cpputest-3.6/include
gcc -c -o sample.o sample.c -g -Wall -std=c99 -D CPPUTEST
g++ -o test test.o sample.o -L/home/wangyubin/Downloads/cpputest-3.6/lib -lCppUTest
$ ./test -v
TEST(sample, ret_int_failed)测试开始......
test.c:44: error: Failure in TEST(sample, ret_int_failed)
expected <3>
but was  <4>
difference starts at position 0 at: <          4         >
^
  测试结束......
  - 0 ms
  TEST(sample, ret_int_success)测试开始......
  测试结束......
  - 0 ms
  Errors (1 failures, 2 tests, 2 ran, 2 checks, 0 ignored, 0 filtered out, 0 ms)
2.7 内存泄漏检测插件
  内存泄漏一直是C/C++代码中令人头疼的问题, 还好, CPPUTest 中提供了检测内存泄漏的插件, 使用这个插件, 可使我们的代码更加健壮.
  使用内存检测插件时, 测试代码 和 待测代码 在编译时都要引用.
  -include $(CPPUTEST_HOME)/include/CppUTest/MemoryLeakDetectorMallocMacros.h
  makefile 修改如下:
# makefile for sample cpputest
CPPUTEST_HOME = /home/wangyubin/Downloads/cpputest-3.6
CC      := gcc
CFLAGS    := -g -Wall
CFLAGS  += -std=c99
CFLAGS  += -D CPPUTEST            # 编译测试文件时, 忽略sample.c的main函数, sample.c的代码中用了宏CPPUTEST
# CPPUTest 是C++写的, 所以用 g++ 来编译 测试文件
CPP     := g++
CPPFLAGS  := -g -Wall
CPPFLAGS  += -I$(CPPUTEST_HOME)/include
LDFLAGS := -L$(CPPUTEST_HOME)/lib -lCppUTest
# 内存泄露检测
MEMFLAGS = -include $(CPPUTEST_HOME)/include/CppUTest/MemoryLeakDetectorMallocMacros.h
sample: sample.o
sample.o: sample.h sample.c
$(CC) -c -o sample.o sample.c $(CFLAGS) $(MEMFLAGS)
# 追加的测试程序编译
test: test.o sample.o
$(CPP) -o $@ test.o sample.o $(LDFLAGS)
test.o: sample.h test.c
$(CPP) -c -o test.o test.c $(CPPFLAGS)  $(MEMFLAGS)
.PHONY: clean
clean:
@echo "clean..."
rm -f test sample
rm -f sample.o test.o
  修改 sample.c 中的 init_student 函数, 构造一个内存泄漏的例子.
/* s->name = name, s->score = score */
void init_student(struct Student* s, char* name, int score)
{
char* name2 = NULL;
name2 = (char*) malloc(sizeof(char) * 80); /* 这里申请的内存, 最后没有释放 */
strcpy(s->name, name2);
strcpy(s->name, name);
s->score = score;
}
  修改 test.c 追加一个测试 init_student 函数的测试用例
  TEST(sample, init_student)
  {
  struct Student *stu = NULL;
  stu = (struct Student*) malloc(sizeof(struct Student));
  char name[80] = {'t', 'e', 's', 't', '\0'};
  init_student(stu, name, 100);
  free(stu);
  }
  执行测试, 可以发现测试结果中提示 sample.c 72 行有内存泄漏风险,
  这一行正是 init_student 函数中用 malloc 申请内存的那一行.
$ make clean
clean...
rm -f test sample
rm -f sample.o test.o
$ make test
g++ -c -o test.o test.c -g -Wall -I/home/wangyubin/Downloads/cpputest-3.6/include  -include /home/wangyubin/Downloads/cpputest-3.6/include/CppUTest/MemoryLeakDetectorMallocMacros.h
gcc -c -o sample.o sample.c -g -Wall -std=c99 -D CPPUTEST             -include /home/wangyubin/Downloads/cpputest-3.6/include/CppUTest/MemoryLeakDetectorMallocMacros.h
g++ -o test test.o sample.o -L/home/wangyubin/Downloads/cpputest-3.6/lib -lCppUTest
$ ./test -v -n init_student
  TEST(sample, init_student)测试开始......
  测试结束......
  test.c:47: error: Failure in TEST(sample, init_student)
Memory leak(s) found.
Alloc num (4) Leak size: 80 Allocated at: sample.c and line: 72. Type: "malloc"
Memory: <0x120c5f0> Content: ""
Total number of leaks:  1
NOTE:
Memory leak reports about malloc and free can be caused by allocating using the cpputest version of malloc,
but deallocate using the standard free.
If this is the case, check whether your malloc/free replacements are working (#define malloc cpputest_malloc etc).
- 0 ms
Errors (1 failures, 3 tests, 1 ran, 0 checks, 0 ignored, 2 filtered out, 0 ms)


最新内容请见作者的GitHub页:http://qaseven.github.io/
相关文章
|
1月前
|
敏捷开发 分布式计算 测试技术
深入理解软件测试中的自动化框架选择与优化策略
【2月更文挑战第29天】 在软件开发的生命周期中,测试环节扮演着至关重要的角色。随着敏捷开发和持续集成的普及,自动化测试成为确保软件质量和加快产品上市速度的关键手段。本文将探讨在构建自动化测试框架时面临的挑战,分析不同类型自动化框架的特点及其适用场景,并提出一系列优化策略,旨在帮助测试工程师提高测试效率,确保测试结果的准确性。
21 0
|
4天前
|
Web App开发 JavaScript 前端开发
深入理解自动化测试框架Selenium的设计与实现
【4月更文挑战第20天】 在软件测试领域,自动化测试已成为提升测试效率和确保产品质量的关键手段。Selenium作为一款广泛使用的开源自动化测试框架,其设计精巧且功能强大,为Web应用提供了一种灵活、高效的测试解决方案。本文将深入探讨Selenium的核心架构与实现细节,解析其如何通过模拟用户操作来执行测试用例,以及它如何适应不断变化的Web技术标准。通过对Selenium内部机制的剖析,旨在帮助测试工程师更好地掌握该工具,并在测试实践中发挥其最大效能。
|
6天前
|
监控 测试技术 数据安全/隐私保护
如何将代理IP集成到自动化测试框架中?
如何将代理IP集成到自动化测试框架中?
|
8天前
|
敏捷开发 监控 前端开发
深入理解自动化测试框架Selenium的架构与实践
【4月更文挑战第16天】 在现代软件开发过程中,自动化测试已成为确保产品质量和加快迭代速度的关键手段。Selenium作为一种广泛使用的自动化测试工具,其开源、跨平台的特性使得它成为业界的首选之一。本文旨在剖析Selenium的核心架构,并结合实际案例探讨其在复杂Web应用测试中的高效实践方法。通过详细解读Selenium组件间的交互机制以及如何优化测试脚本,我们希望为读者提供深入理解Selenium并有效运用于日常测试工作的参考。
14 1
|
9天前
|
自然语言处理 测试技术 API
深入理解自动化测试框架Selenium的设计理念与实践
【4月更文挑战第15天】 在现代软件开发过程中,自动化测试已成为确保产品质量和加速迭代的关键手段。Selenium作为一种广泛使用的自动化测试框架,提供了对多种浏览器和平台的支持,极大地促进了Web应用的功能测试。本文旨在剖析Selenium的核心设计理念,探讨其在实际项目中的应用,并指出常见的误区及最佳实践,以期帮助测试工程师更高效地利用Selenium进行测试工作。
|
10天前
|
监控 测试技术 API
深入理解自动化测试框架Selenium的设计与实现
【4月更文挑战第14天】在软件开发过程中,自动化测试是确保代码质量、减少人工重复劳动的关键步骤。Selenium作为一款广泛使用的自动化测试工具,提供了对多种浏览器和操作系统的支持。本文将探讨Selenium的核心组件及其架构设计,分析其如何通过WebDriver与浏览器交互,以及它如何支持多种编程语言进行脚本编写。同时,我们还将讨论Selenium Grid的作用以及它如何实现并行测试,以缩短测试周期并提高测试效率。
176 59
|
12天前
|
Web App开发 前端开发 Java
框架分析(11)-测试框架
框架分析(11)-测试框架
|
26天前
|
敏捷开发 设计模式 监控
深入理解自动化测试框架的设计原则
在软件开发的复杂多变环境中,自动化测试已成为确保产品质量和加速市场交付的关键步骤。本文将探讨自动化测试框架的设计原则,包括模块化、可扩展性、易用性和可靠性,旨在为软件测试工程师提供构建高效、健壮且易于维护的自动化测试系统的指导。通过分析设计模式的应用,我们将了解如何减少代码冗余,提高测试覆盖率,并适应快速变化的技术要求。
|
27天前
|
前端开发 IDE JavaScript
深入理解自动化测试框架Selenium的设计与实现
本文旨在探讨开源自动化测试框架Selenium的核心设计及其实现机制。通过分析其架构、组件和工作原理,揭示Selenium如何有效地支持跨浏览器、跨平台的自动化Web测试。文中不仅介绍了Selenium的主要功能模块,还详细讨论了其面临的挑战及应对策略,为读者提供了深入了解和使用Selenium的理论基础和实践指导。
|
29天前
|
敏捷开发 测试技术 持续交付
深入探索软件测试自动化:框架与实践
在快速演进的软件行业中,测试自动化已成为确保产品质量和加快上市速度的关键因素。本文将深入分析测试自动化框架的构建要点,探讨其在实际应用中的效益,以及实施过程中可能面临的挑战。通过对比手动测试与自动化测试的优势与局限,本文旨在为读者提供一套系统化的测试自动化实践指南,以支持更高效、可靠的软件开发周期。
11 0

热门文章

最新文章