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/
相关文章
|
4天前
|
机器学习/深度学习 前端开发 测试技术
探索软件测试中的自动化测试框架选择与优化策略####
本文深入探讨了在当前软件开发生命周期中,自动化测试框架的选择对于提升测试效率、保障产品质量的重要性。通过分析市场上主流的自动化测试工具,如Selenium、Appium、Jest等,结合具体项目需求,提出了一套系统化的选型与优化策略。文章首先概述了自动化测试的基本原理及其在现代软件开发中的角色变迁,随后详细对比了各主流框架的功能特点、适用场景及优缺点,最后基于实际案例,阐述了如何根据项目特性量身定制自动化测试解决方案,并给出了持续集成/持续部署(CI/CD)环境下的最佳实践建议。 --- ####
|
18天前
|
测试技术 C# 数据库
C# 单元测试框架 NUnit 一分钟浅谈
【10月更文挑战第17天】单元测试是软件开发中重要的质量保证手段,NUnit 是一个广泛使用的 .NET 单元测试框架。本文从基础到进阶介绍了 NUnit 的使用方法,包括安装、基本用法、参数化测试、异步测试等,并探讨了常见问题和易错点,旨在帮助开发者有效利用单元测试提高代码质量和开发效率。
123 64
|
5天前
|
Java 测试技术 持续交付
【入门思路】基于Python+Unittest+Appium+Excel+BeautifulReport的App/移动端UI自动化测试框架搭建思路
本文重点讲解如何搭建App自动化测试框架的思路,而非完整源码。主要内容包括实现目的、框架设计、环境依赖和框架的主要组成部分。适用于初学者,旨在帮助其快速掌握App自动化测试的基本技能。文中详细介绍了从需求分析到技术栈选择,再到具体模块的封装与实现,包括登录、截图、日志、测试报告和邮件服务等。同时提供了运行效果的展示,便于理解和实践。
24 4
【入门思路】基于Python+Unittest+Appium+Excel+BeautifulReport的App/移动端UI自动化测试框架搭建思路
|
4天前
|
测试技术 API Android开发
探索软件测试中的自动化框架选择与实践####
本文深入探讨了软件测试领域内,面对众多自动化测试框架时,如何依据项目特性和团队需求做出明智选择,并分享了实践中的有效策略与技巧。不同于传统摘要的概述方式,本文将直接以一段实践指南的形式,简述在选择自动化测试框架时应考虑的核心要素及推荐路径,旨在为读者提供即时可用的参考。 ####
|
8天前
|
测试技术 Android开发 UED
探索软件测试中的自动化框架选择
【10月更文挑战第29天】 在软件开发的复杂过程中,测试环节扮演着至关重要的角色。本文将深入探讨自动化测试框架的选择,分析不同框架的特点和适用场景,旨在为软件开发团队提供决策支持。通过对比主流自动化测试工具的优势与局限,我们将揭示如何根据项目需求和团队技能来选择最合适的自动化测试解决方案。此外,文章还将讨论自动化测试实施过程中的关键考虑因素,包括成本效益分析、维护难度和扩展性等,确保读者能够全面理解自动化测试框架选择的重要性。
24 1
|
14天前
|
监控 安全 jenkins
探索软件测试的奥秘:自动化测试框架的搭建与实践
【10月更文挑战第24天】在软件开发的海洋里,测试是确保航行安全的灯塔。本文将带领读者揭开软件测试的神秘面纱,深入探讨如何从零开始搭建一个自动化测试框架,并配以代码示例。我们将一起航行在自动化测试的浪潮之上,体验从理论到实践的转变,最终达到提高测试效率和质量的彼岸。
|
18天前
|
Web App开发 敏捷开发 存储
自动化测试框架的设计与实现
【10月更文挑战第20天】在软件开发的快节奏时代,自动化测试成为确保产品质量和提升开发效率的关键工具。本文将介绍如何设计并实现一个高效的自动化测试框架,涵盖从需求分析到框架搭建、脚本编写直至维护优化的全过程。通过实例演示,我们将探索如何利用该框架简化测试流程,提高测试覆盖率和准确性。无论你是测试新手还是资深开发者,这篇文章都将为你提供宝贵的洞见和实用的技巧。
|
6天前
|
机器学习/深度学习 自然语言处理 物联网
探索自动化测试框架的演变与未来趋势
随着软件开发行业的蓬勃发展,软件测试作为保障软件质量的重要环节,其方法和工具也在不断进化。本文将深入探讨自动化测试框架从诞生至今的发展历程,分析当前主流框架的特点和应用场景,并预测未来的发展趋势,为软件开发团队选择合适的自动化测试解决方案提供参考。
|
1月前
|
Web App开发 IDE 测试技术
自动化测试的利器:Selenium 框架深度解析
【10月更文挑战第2天】在软件开发的海洋中,自动化测试犹如一艘救生艇,让质量保证的过程更加高效与精准。本文将深入探索Selenium这一强大的自动化测试框架,从其架构到实际应用,带领读者领略自动化测试的魅力和力量。通过直观的示例和清晰的步骤,我们将一起学习如何利用Selenium来提升软件测试的效率和覆盖率。
|
9天前
|
测试技术 持续交付
探索软件测试中的自动化框架:优势与挑战
【10月更文挑战第28天】 随着软件开发的快速进步,自动化测试已成为确保软件质量的关键步骤。本文将探讨自动化测试框架的优势和面临的挑战,以及如何有效地克服这些挑战。
24 0