linux-单元测试--Cunit使用

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

[cpp]  view plain  copy
  1. /* strformat.h ---  
  2.  *  
  3.  * Filename: strformat.h 
  4.  * Description: 字符串操作头文件 
  5.  * Author: hnlyyk 
  6.  * Maintainer:  
  7.  * Created: 一  8月 20 22:57:19 2012 (+0800) 
  8.  * Version:  
  9.  * Compatibility:  
  10.  *  
  11.  */  
  12. /* Commentary:  
  13.  * 为的是体验Cunit而临时写的几项功能函数,没有多大实际意义,仅仅是为了写测试类 
  14.  *  
  15.  *  
  16.  */  
  17.   
  18. /* Change Log: 
  19.  *  
  20.  *  
  21.  */  
  22.   
  23. /* Code: */  
  24.   
  25. #ifndef _strformat_h  
  26. #define _strformat_h  
  27.   
  28. typedef char * string;  
  29.   
  30. /************************************************************************* 
  31. *功能描述:返回字符串的长度 
  32. *参数列表: 
  33. *返回类型: 
  34. **************************************************************************/      
  35. int string_lenth(string word);  
  36. /************************************************************************* 
  37. *功能描述:返回字符串的大写形式 
  38. *参数列表: 
  39. *返回类型: 
  40. **************************************************************************/  
  41. string to_Upper(string word);  
  42. /************************************************************************* 
  43. *功能描述:连接字符串 
  44. *参数列表: 
  45. *返回类型: 
  46. **************************************************************************/  
  47. string add_str(string word1 ,string word2);  
  48.   
  49.   
  50.   
  51. #endif  
  52.   
  53.   
  54. /* strformat.h ends here */  
代码:strformat.c

[cpp]  view plain  copy
  1. /* strformat.c ---  
  2.  *  
  3.  * Filename: strformat.c 
  4.  * Description: 字符串操作 
  5.  * Author: hnlyyk 
  6.  * Maintainer:  
  7.  * Keywords:  
  8.  * Compatibility:  
  9.  *  
  10.  */  
  11.   
  12. /* Commentary:  
  13.  * 此代码仅为体验Cunit而临时撰写。 
  14.  *  
  15.  *  
  16.  */  
  17.   
  18. /* Change Log: 
  19.  *  
  20.  *  
  21.  */  
  22.   
  23. /* Code: */  
  24. #include <assert.h>  
  25. #include <ctype.h>  
  26. #include <errno.h>  
  27. #include <limits.h>  
  28. #include <string.h>  
  29. #include <stdarg.h>  
  30. #include <stdlib.h>  
  31. #include <stdio.h>  
  32. #include "strformat.h"  
  33.   
  34.   
  35. /************************************************************************** 
  36. 函数名称:字符串相加 
  37. 功能描述: 
  38. 输入参数: 
  39. 返   回: 
  40. **************************************************************************/  
  41. string add_str(string word1 ,string word2){  
  42.     return (strcat(word1, word2));  
  43. }  
  44.   
  45. /************************************************************************** 
  46. 函数名称:将字符串转换成大写格式 
  47. 功能描述: 
  48. 输入参数: 
  49. 返   回: 
  50. **************************************************************************/  
  51. string to_Upper(string word){  
  52.     int i;  
  53.     for(i = 0;word[i] !='\0' ;i++){  
  54.         if(word[i]<'z' && word[i]>'a'){  
  55.             word[i] -= 32;  
  56.         }  
  57.     }  
  58.     return word;  
  59.       
  60. }  
  61.   
  62. /************************************************************************** 
  63. 函数名称:字符串长度 
  64. 功能描述: 
  65. 输入参数: 
  66. 返   回: 
  67. **************************************************************************/  
  68. int string_lenth(string word){  
  69.     int i;  
  70.     for(i = 0 ;word[i] != '\0';i++){  
  71.           
  72.     }  
  73.     return i;  
  74. }  
  75.   
  76. /* strformat.c ends here */  
测试代码: testcase.c
[cpp]  view plain  copy
  1. /* Commentary: 
  2.  * 当前文件用来定义测试方法,suite,及registry信息,若测试方法有变化,只需要修改当前文件即可。 
  3.  * 第一步:书写测试函数的代码,建议以"test_"为前缀。 
  4.  * 第二步:将测试方法归类,即将相似功能的测试方法放到一个数组里,以便把它们指定给一个suite 
  5.  * 第三步:创建suite,可按功能或模块,生成多个test suite, 
  6.  * 第四步:书写测试方法的总调用方法,AddTests(),用来统一启动测试方法。 
  7.  */  
  8.   
  9. /* Change Log: 
  10.  *  
  11.  *  
  12.  */  
  13.   
  14. /* Code: */  
  15. #include <assert.h>  
  16. #include <ctype.h>  
  17. #include <errno.h>  
  18. #include <limits.h>  
  19. #include <string.h>  
  20. #include <stdarg.h>  
  21. #include <stdlib.h>  
  22. #include <stdio.h>  
  23.   
  24. #include <CUnit/Basic.h>  
  25. #include <CUnit/Console.h>  
  26. #include <CUnit/CUnit.h>  
  27. #include <CUnit/TestDB.h>  
  28. #include "strformat.h"  
  29.   
  30. /************************************************************************** 
  31. 函数名称:测试string_lenth()方法 
  32. 功能描述: 
  33. 输入参数: 
  34. 返   回: 
  35. **************************************************************************/  
  36. void test_string_lenth(void){  
  37.     string test = "Hello";  
  38.     int len = string_lenth(test);  
  39.     CU_ASSERT_EQUAL(len,5);  
  40. }  
  41.   
  42. /************************************************************************** 
  43. 函数名称:测试方法to_Upper() 
  44. 功能描述: 
  45. 输入参数: 
  46. 返   回: 
  47. **************************************************************************/  
  48.   
  49. void test_to_Upper(void){  
  50.     char test[] = "Hello";  
  51.     CU_ASSERT_STRING_EQUAL(to_Upper(test),"HELLO");  
  52.       
  53. }  
  54.   
  55. /************************************************************************** 
  56. 函数名称:测试方法 add_str() 
  57. 功能描述: 
  58. 输入参数: 
  59. 返   回: 
  60. **************************************************************************/  
  61. void test_add_str(void){  
  62.     char test1[] = "Hello!";  
  63.     char test2[] = "MGC";  
  64.     CU_ASSERT_STRING_EQUAL(add_str(test1,test2),"Hello!MGC");  
  65.       
  66. }  
  67.   
  68. /************************************************************************** 
  69. 数组名称:将多个测试方法打包成组,以供指定给一个Suite 
  70. 功能描述:每个suite可以有一个或多个测试方法,以CU_TestInfo数组形式指定 
  71. **************************************************************************/  
  72. // CU_TestInfo是Cunit内置的一个结构体,它包括测试方法及描述信息  
  73. CU_TestInfo testcase[] = {  
  74.     {"test_for_lenth:",test_string_lenth    },  
  75.     {"test_for_add:",test_add_str    },  
  76.     CU_TEST_INFO_NULL  
  77. };  
  78.   
  79. CU_TestInfo testcase2[] = {  
  80.     {"test for Upper :",test_to_Upper    },  
  81.     CU_TEST_INFO_NULL  
  82. };  
  83.   
  84. /************************************************************************** 
  85. 函数名称:suite初始化过程 
  86. 功能描述: 
  87. 输入参数: 
  88. 返   回: 
  89. **************************************************************************/  
  90. int suite_success_init(void){  
  91.     return 0;  
  92.       
  93. }  
  94.   
  95. /************************************************************************** 
  96. 函数名称:suite清理过程,以便恢复原状,使结果不影响到下次运行 
  97. 功能描述: 
  98. 输入参数: 
  99. 返   回: 
  100. **************************************************************************/  
  101. int suite_success_clean(void){  
  102.     return 0;  
  103. }  
  104.   
  105. //定义suite数组,包括多个suite,每个suite又会包括若干个测试方法。  
  106. CU_SuiteInfo suites[] = {  
  107.     {"testSuite1",suite_success_init,suite_success_clean,testcase},  
  108.     {"testsuite2",suite_success_init,suite_success_clean,testcase2},  
  109.     CU_SUITE_INFO_NULL  
  110. };  
  111.   
  112. /************************************************************************** 
  113. 函数名称:测试类的调用总接口 
  114. 功能描述: 
  115. 输入参数: 
  116. 返   回: 
  117. **************************************************************************/  
  118. void AddTests(){  
  119.     assert(NULL != CU_get_registry());  
  120.     assert(!CU_is_test_running());  
  121.   
  122.     if(CUE_SUCCESS != CU_register_suites(suites)){  
  123.         exit(EXIT_FAILURE);  
  124.   
  125.     }  
  126. }  
  127. /************************************************************************* 
  128. *功能描述:运行测试入口 
  129. *参数列表: 
  130. *返回类型: 
  131. **************************************************************************/  
  132.   
  133. int RunTest(){  
  134.         if(CU_initialize_registry()){  
  135.                 fprintf(stderr, " Initialization of Test Registry failed. ");  
  136.                 exit(EXIT_FAILURE);  
  137.         }else{  
  138.                 AddTests();  
  139.                 /**** Automated Mode ***************** 
  140.                 CU_set_output_filename("TestMax"); 
  141.                 CU_list_tests_to_file(); 
  142.                 CU_automated_run_tests(); 
  143.                 //************************************/  
  144.                   
  145.                 /***** Basice Mode ******************* 
  146.                 CU_basic_set_mode(CU_BRM_VERBOSE); 
  147.                 CU_basic_run_tests(); 
  148.                 //************************************/  
  149.   
  150.                 /*****Console Mode ******************** 
  151.                 CU_console_run_tests(); 
  152.                 //************************************/  
  153.   
  154.                 CU_cleanup_registry();  
  155.                   
  156.                 return CU_get_error();  
  157.                   
  158.         }  
  159.   
  160. }  
  161. /************************************************************************* 
  162. *功能描述:测试类主方法 
  163. *参数列表: 
  164. *返回类型: 
  165. **************************************************************************/  
  166.   
  167. int main(int argc, char * argv[])  
  168. {  
  169.    return  RunTest();  
  170.       
  171. }  
  172.   
  173. /* testcase.c ends here */  
注:
1)注意结合上面Cunit的组织结构图,理解Cunit中几个角色的关系(CU_TestInfo,CU_SuiteInfo各以数组的形式,将多个Test和Suite组织起来)。
2)Cunit有几种运行模式,如automated,basic,console,有的是可以交互的,有的是没有交互,直接出结果的。

代码:makefile

[cpp]  view plain  copy
  1. IINC=-I/usr/local/include/CUnit  
  2. LIB=-L/usr/local/lib/  
  3.   
  4. all:  strformat.c testcase.c  
  5.     gcc -o test $(INC) $(LIB)  $^ -lcunit -static  

下面我们欣赏一下Cunit的常见几种运行模式
1)Automated Mode
先将testcase.c中156~159代码放开注释,此时便是以automated模式运行,此模块没有交互能力,直接生成XML格式的报表,先make,然后运行后,在当前目录下生成两个报表
TestMax-Listing.xml和TestMax-Results.xml(前者是测试用例的列表,后者是测试用例的测试结果) ,但这两个文件是不能直接观看的,要查看这两个文件,需要使用如下xsl和dtd文件:CUnit-List.dtd和CUnit-List.xsl用于解析列表文件, CUnit-Run.dtd和CUnit-Run.xsl用于解析结果文件。这四个文件在CUnit包里面有提供,安装之后在$(PREFIX) /share/CUnit目录下,默认安装的话在/home/lirui/local/share/CUnit目录下。在查看结果之前,需要把这六 个文件:TestMax-Listing.xml, TestMax-Results.xml, CUnit-List.dtd, CUnit-List.xsl, CUnit-Run.dtd, CUnit-Run.xsl拷贝到一个目录下,然后用浏览器打开两个结果的xml文件就可以了。

2) Console Mode
在testcase.c中将其它模式代码注释掉,放开168行代码,便转换成Console模式了。console模式支持交互,如支持运行,查看错误等操作。输入R来运行,输入F来查看错误用例,输入Q来退出。
这种模式在实际中是很实用的。

3)Basic Mode
在testcase.c中将其它模式的代码注释掉,放到163~164行代码,便转换成Basic模式,这种是不支持交互的,直接打印出运行结果。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值