1.CUNIT 介绍
CUnit是一种C语言单元测试框架 ,继Junit CppUnit的成功后, c语言环境下也出现了开发源码的白盒测试用例CUnit。CUnit以静态库的形式提供给用户使用,用户编写程序的时候直接链接此静态库就可以了。它提供了一个简单的单元测试框架,并且为常用的数据类型提供了丰富的断言语句支持。
2.CUNIT的下载和安装(linux)
2.1、首先在
站点1:http://www.hnlyyk.icoc.cc/col.jsp?id=103,下载cunit源码包:cunit-2.0-1.tar
站点2:http://download.csdn.net/download/u010193457/9193477 ,下载最新版本的CUnit源码包
2.2、将CUnit源码包(cunit-2.0-1.tar)复制到Linux的目标目录下,比如我在这里放到了/usr/unittest目录下。
2.3、CUnit源码包的解压。打开[System Tools]-〉[Terminal],进入到/usr/unittest目录下,
输入如下命令:
#tar xvf cunit-2.0-1.tar
执行结束后,将会在当前目录下生成一个解压后的文件夹(cunit-2.0-1)。
2.4、解压结束后,开始进行编译和安装。
#cd cunit-2.0-1
#aclocal
#autoconf
#automake
#chmod u+x configure
#./configure --prefix
(对上一句进行解释,这个位置,需要你输入要安装的目录,目录的格式举例如下:/usr/unittest/)
#make
#make install
这里需要一段时间...
#cd /usr/unittest/lib
#ldconfig
到此处为止,CUnit的安装基本上就到一段落了。
3.CUNIT结构框架
CUnit的测试是单线程启动,只能注册一个Test Registry, 一次测试(Test Registry)可以运行多个测试包(Test Suite),而每个测试包可以包括多个测试用例(Test Case),每个测试用例又包含一个或者多个断言类的语句。具体到程序的结构上,一次测试下辖多个Test Suite,它对应于程序中各个独立模块;一个Suite管理多个Test Case,它对应于模块内部函数实现。每个Suite可以含有setup和teardown函数,分别在执行suite的前后调用。
注册一个测试用例(如果已经注册了你可以cleanup掉然后重新注册使用)然后CU_add_suite增加你的模块然后CU_add_test再在你的模块下挂载你的模块内的测试函数。所有的挂载完毕后,调用你想使用的界面进行测试。
4.CUNIT测试模式
下面是四种测试模式:
1 Automated Output to xml file Non-interactive
2 Basic Flexible programming interface Non-interactive
3 Console Console interface (ansi C) Interactive
4 Curses Graphical interface (Unix) Interact
注意1,2是没有交互功能的,4的Unix下的我是windows用户, 选择第3个介绍console而且console是可以人机交互的。
5.CUNIT测试流程
使用CUnit进行测试的基本流程如下所示:
⒈书写代测试的函数(如果必要,需要写suite的init/cleanup函数)
⒉初始化Test Registry - CU_initialize_registry()
⒊把测试包(Test Suites)加入到Test Registry - CU_add_suite()
⒋加入测试用例(Test Case)到测试包当中 - CU_add_test()
⒌使用适当的接口来运行测试测试程序,例如 CU_console_run_tests()
⒍清除Test Registry - CU_cleanup_registry()
⒈4 TestCase的构成
一个CUnit TestCase的构成有以下文件:test.c、testcase.c、Main.c 与 Makefile构成。即一个测试范例由①被测函数,②测试函数(定义测试用例和测试包),③运行测试函数及④Makefile四部分构成。
6.CUNIT构成
CUnit TestCase的构成
一个CUnit TestCase的构成有以下文件:test.c、testcase.c、Main.c 与 Makefile构成。
主要构成函数说明
以下以一个简单的testcase为例说明。
我要测试的是整数求最大值的函数maxi,我使用如下文件组织结构:
⒈test.c:被测函数(定义maxi()函数)
⒉testcase.c:测试函数(定义测试用例和测试包)
⒊Main.c:运行测试函数(调用CUnit的Automated接口运行测试)
7.CUNIT函数介绍
1. registry初始化
CU_ErrorCode CU_initialize_registry(void);
//用户在调用任何其他CUnit函数之前调用本函数,如果不这样做可能会导致系统崩溃。
返回值为:
CUE_SUCCESS初始化成功。
CUE_NOMEMORY内存分配失败。
2. registry释放
void CU_cleanup_registry(void);
//当测试完成后,用户应该调用这个函数进行清理和释放的框架所使用的内存。
//其他一些关于注册的内部函数,主要用于内部和测试的目的,较少使用
CU_pTestRegistry CU_get_registry(void);
CU_pTestRegistry CU_set_registry(CU_pTestRegistry pTestRegistry);
CU_pTestRegistry CU_create_new_registry(void);
void CU_destroy_existing_registry(CU_pTestRegistry* ppRegistry);
3. 向registry中增加suites
CU_pSuite CU_add_suite(const char* strName, CU_InitializeFunc pInit, CU_CleanupFunc pClean);
//创建一个新的测试集,具有指定的名称、初始化函数、清除函数。
返回值为:
l CUE_SUCCESS suite创建成功。
l CUE_NOREGISTRY registry尚未初始化。
l CUE_NO_SUITENAME strName为NULL。
l CUE_DUP_SUITE suite的名称已经存在。
l CUE_NOMEMORY 内存分配失败。
4. 向registry中增加test
CU_pTest CU_add_test(CU_pSuite pSuite, const char* strName, CU_TestFunc pTestFunc)
//创建一个新的具有指定名称和测试功能的test,并在指定的suite中注册。该suite必须是已经使用CU_add_suite()创建的。
返回值为:
l CUE_SUCCESS suite创建成功。
l CUE_NOSUITE 指定的suite是Null或无效。
l CUE_NO_TESTNAME strName为NULL。
l CUE_NO_TEST pTestFunc为null或无效。
l CUE_DUP_TEST test的名称已存在。
l CUE_NOMEMORY 内存分配失败。
5. 运行Tests
1) Automated Mode 自动输出到XML文件
非交互式
void CU_automated_run_tests(void);
//运行在所有suites中注册的所有tests
CU_ErrorCode CU_list_tests_to_file(void);
//列出已注册的suites和与其相关联的tests
void CU_set_output_filename(const char* szFilenameRoot);
//设置-Results.xml和-Listing.xml的名字
2) Basic Mode 基本扩展编程方式
非交互式
CU_ErrorCode CU_basic_run_tests(void);
//运行在所有suites中注册的所有tests
CU_ErrorCode CU_basic_run_suite(CU_pSuite pSuite);
//在一个指定的suite中运行其下所有的tests
CU_ErrorCode CU_basic_run_test(CU_pSuite pSuite, CU_pTest pTest);
//在一个指定的suite中运行指定的test
void CU_basic_set_mode(CU_BasicRunMode mode);
//设置运行的基本模式,控制着tests的输出,主要有下面几种
l CU_BRM_NORMAL 失败和运行概况打印。
l CU_BRM_SILENT 只打印错误消息。
l CU_BRM_VERBOSE Maximum output of run details。
CU_BasicRunMode CU_basic_get_mode(void);
//检测当前的运行模式
void CU_basic_show_failures(CU_pFailureRecord pFailure);
//将所有的失败信息打印到stdout,不依赖于运行模式
3) Interactive Console Mode 控制台方式
交互式
void CU_console_run_tests(void);
//启动控制台方式
4) Interactive Curses Mode Curses图形接口
交互式,只适用于Unix/Linux
void CU_curses_run_tests(void);
//启动Curses方式,需要应用程序中支持ncurses库支持
6. 获取接口目前测试运行的结果
用户代码有时可能需要这些结果。主要包含以下函数:
unsigned int CU_get_number_of_suites_run(void)
unsigned int CU_get_number_of_suites_failed(void)
unsigned int CU_get_number_of_tests_run(void)
unsigned int CU_get_number_of_tests_failed(void)
unsigned int CU_get_number_of_asserts(void)
unsigned int CU_get_number_of_successes(void)
unsigned int CU_get_number_of_failures(void)
const CU_pRunSummary CU_get_run_summary(void)
const CU_pFailureRecord CU_get_failure_list(void)
unsigned int CU_get_number_of_failure_records(void)
7. 错误处理
CU_ErrorCode CU_get_error(void)
const char* CU_get_error_msg(void)
//第一个函数返回错误代码本身,而第二个函数返回一条消息,描述错误信息
void CU_set_error_action(CU_ErrorAction action)
CU_ErrorAction CU_get_error_action(void)
8.CUNIT案例演示
下面我们结合一个小实例,来体验一下Cunit的便利吧(学编程,最有效的方式还是自己动手)
先编写一个具体两个简单功能的函数,然后写Testcase来测试它。
文件主要有:
1) strformat.h :字符串功能函数的接口文件
2)strformat.c :字符串功能函数的实现
3)testcase.c : 测试用例及Cunit运行环境
4)makefile :
代码:strformat.h
/* strformat.h ---
*
* Filename: strformat.h
* Description: 字符串操作头文件
* Author: hnlyyk
* Maintainer:
* Created: 一 8月 20 22:57:19 2012 (+0800)
* Version:
* Compatibility:
*
*/
/* Commentary:
* 为的是体验Cunit而临时写的几项功能函数,没有多大实际意义,仅仅是为了写测试类
*
*
*/
/* Change Log:
*
*
*/
/* Code: */
#ifndef _strformat_h
#define _strformat_h
typedef char * string;
/*************************************************************************
*功能描述:返回字符串的长度
*参数列表:
*返回类型:
**************************************************************************/
int string_lenth(string word);
/*************************************************************************
*功能描述:返回字符串的大写形式
*参数列表:
*返回类型:
**************************************************************************/
string to_Upper(string word);
/*************************************************************************
*功能描述:连接字符串
*参数列表:
*返回类型:
**************************************************************************/
string add_str(string word1 ,string word2);
#endif
/* strformat.h ends here */
代码:strformat.c
/* strformat.c ---
*
* Filename: strformat.c
* Description: 字符串操作
* Author: hnlyyk
* Maintainer:
* Keywords:
* Compatibility:
*
*/
/* Commentary:
* 此代码仅为体验Cunit而临时撰写。
*
*
*/
/* Change Log:
*
*
*/
/* Code: */
#include
#include
#include
#include
#include
#include
#include
#include
#include "strformat.h"
/**************************************************************************
函数名称:字符串相加
功能描述:
输入参数:
返 回:
**************************************************************************/
string add_str(string word1 ,string word2){
return (strcat(word1, word2));
}
/**************************************************************************
函数名称:将字符串转换成大写格式
功能描述:
输入参数:
返 回:
**************************************************************************/
string to_Upper(string word){
int i;
for(i = 0;word[i] !='\0' ;i++){
if(word[i]<'z' && word[i]>'a'){
word[i] -= 32;
}
}
return word;
}
/**************************************************************************
函数名称:字符串长度
功能描述:
输入参数:
返 回:
**************************************************************************/
int string_lenth(string word){
int i;
for(i = 0 ;word[i] != '\0';i++){
}
return i;
}
/* strformat.c ends here */
测试代码: testcase.c
/* Commentary:
* 当前文件用来定义测试方法,suite,及registry信息,若测试方法有变化,只需要修改当前文件即可。
* 第一步:书写测试函数的代码,建议以"test_"为前缀。
* 第二步:将测试方法归类,即将相似功能的测试方法放到一个数组里,以便把它们指定给一个suite
* 第三步:创建suite,可按功能或模块,生成多个test suite,
* 第四步:书写测试方法的总调用方法,AddTests(),用来统一启动测试方法。
*/
/* Change Log:
*
*
*/
/* Code: */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "strformat.h"
/**************************************************************************
函数名称:测试string_lenth()方法
功能描述:
输入参数:
返 回:
**************************************************************************/
void test_string_lenth(void){
string test = "Hello";
int len = string_lenth(test);
CU_ASSERT_EQUAL(len,5);
}
/**************************************************************************
函数名称:测试方法to_Upper()
功能描述:
输入参数:
返 回:
**************************************************************************/
void test_to_Upper(void){
char test[] = "Hello";
CU_ASSERT_STRING_EQUAL(to_Upper(test),"HELLO");
}
/**************************************************************************
函数名称:测试方法 add_str()
功能描述:
输入参数:
返 回:
**************************************************************************/
void test_add_str(void){
char test1[] = "Hello!";
char test2[] = "MGC";
CU_ASSERT_STRING_EQUAL(add_str(test1,test2),"Hello!MGC");
}
/**************************************************************************
数组名称:将多个测试方法打包成组,以供指定给一个Suite
功能描述:每个suite可以有一个或多个测试方法,以CU_TestInfo数组形式指定
**************************************************************************/
// CU_TestInfo是Cunit内置的一个结构体,它包括测试方法及描述信息
CU_TestInfo testcase[] = {
{"test_for_lenth:",test_string_lenth },
{"test_for_add:",test_add_str },
CU_TEST_INFO_NULL
};
CU_TestInfo testcase2[] = {
{"test for Upper :",test_to_Upper },
CU_TEST_INFO_NULL
};
/**************************************************************************
函数名称:suite初始化过程
功能描述:
输入参数:
返 回:
**************************************************************************/
int suite_success_init(void){
return 0;
}
/**************************************************************************
函数名称:suite清理过程,以便恢复原状,使结果不影响到下次运行
功能描述:
输入参数:
返 回:
**************************************************************************/
int suite_success_clean(void){
return 0;
}
//定义suite数组,包括多个suite,每个suite又会包括若干个测试方法。
CU_SuiteInfo suites[] = {
{"testSuite1",suite_success_init,suite_success_clean,testcase},
{"testsuite2",suite_success_init,suite_success_clean,testcase2},
CU_SUITE_INFO_NULL
};
/**************************************************************************
函数名称:测试类的调用总接口
功能描述:
输入参数:
返 回:
**************************************************************************/
void AddTests(){
assert(NULL != CU_get_registry());
assert(!CU_is_test_running());
if(CUE_SUCCESS != CU_register_suites(suites)){
exit(EXIT_FAILURE);
}
}
/*************************************************************************
*功能描述:运行测试入口
*参数列表:
*返回类型:
**************************************************************************/
int RunTest(){
if(CU_initialize_registry()){
fprintf(stderr, " Initialization of Test Registry failed. ");
exit(EXIT_FAILURE);
}else{
AddTests();
/**** Automated Mode *****************
CU_set_output_filename("TestMax");
CU_list_tests_to_file();
CU_automated_run_tests();
//************************************/
/***** Basice Mode *******************
CU_basic_set_mode(CU_BRM_VERBOSE);
CU_basic_run_tests();
//************************************/
/*****Console Mode ********************
CU_console_run_tests();
//************************************/
CU_cleanup_registry();
return CU_get_error();
}
}
/*************************************************************************
*功能描述:测试类主方法
*参数列表:
*返回类型:
**************************************************************************/
int main(int argc, char * argv[])
{
return RunTest();
}
/* testcase.c ends here */
注:
代码:makefile
IINC=-I/usr/local/include/CUnit
LIB=-L/usr/local/lib/
all: strformat.c testcase.c
gcc -o test $(INC) $(LIB) $^ -lcunit -static
2) Console Mode
在testcase.c中将其它模式代码注释掉,放开168行代码,便转换成Console模式了。console模式支持交互,如支持运行,查看错误等操作。输入R来运行,输入F来查看错误用例,输入Q来退出。
这种模式在实际中是很实用的。
3)Basic Mode
在testcase.c中将其它模式的代码注释掉,放到163~164行代码,便转换成Basic模式,这种是不支持交互的,直接打印出运行结果。