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 <assert.h> #include <ctype.h> #include <errno.h> #include <limits.h> #include <string.h> #include <stdarg.h> #include <stdlib.h> #include <stdio.h> #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 <assert.h> #include <ctype.h> #include <errno.h> #include <limits.h> #include <string.h> #include <stdarg.h> #include <stdlib.h> #include <stdio.h> #include <CUnit/Basic.h> #include <CUnit/Console.h> #include <CUnit/CUnit.h> #include <CUnit/TestDB.h> #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模式,这种是不支持交互的,直接打印出运行结果。