1、CUnit简介
CUnit 是一个通过 C 语言编写的用于编写、管理和执行单元测试用例的C语言单元测试框架。
它往往被编成库的形式(静态库或动态库)提供给用户测试代码进行使用,用户编写程序的时候直接链接此静态库就可以了。
它提供了一个简单的单元测试框架,并且为常用的数据类型提供了丰富的断言语句支持。
CUnit是平台无关的框架与各种用户接口的组合。核心框架为管理测试注册表,套件和测试用例提供了基本支持。用户接口便于与框架交互以运行测试和查看结果。CUnit的组织结构与传统的单元测试框架类似:
单独的测试用例(Test)被打包到套件(Suite)中,这些套件又被注册到活动测试注册表(Test Registry)里。每个套件都有自己的构造和析构函数,这两个函数将在运行套件测试之前和之后被自动调用。注册表中的所有套件/测试用例,可以通过调用一个函数执行全部测试,也可以有选择性地执行部分测试。
2、CUnit安装
2.1、安装配置——Ubuntu
软件源中与CUnit相关的包有:
libcunit1 libcunit1-dev libcunit1-doc libcunit1-ncurses libcunit1-ncurses-dev
使用apt-get install安装即可。
2.2、安装步骤
1、tar -jxvf CUnit-2.1-3.tar.bz2
2、cd CUnit-2.1-3
3、libtoolize -f -c -i
4、aclocal
5、autoconf
6、autoheader
7、automake
8、chmod u+x configure
9、./configure --prefix=/opt/cunit
10、make
11、make install
2.3、安装文件
cunit安装成功后会生成4个文件夹:doc、include、lib、share。
doc目录是一些简介以及使用说明。
include和lib目录中是我们需要的头文件以及库文件。
share目录中有Automated模式下需要的文件。
3、CUnit使用介绍
3.1、输出方式
接口 | 平台 | 描述 |
---|---|---|
Automated | All | 非交互式,输出XML文件 |
Basic | All | 非交互式,可选输出到stdout |
Console | All | 控制台交互方式,在用户控制下运行 |
Curses | Linux/Unix | Curses交互方式(图形界面),在用户控制下运行 |
如上边所示,后两种主要是interactive的接口,就是我们可以交互式地指定参数,然后观察结果。在我们具体的环境下,我们通常使用上面两种接口,第一种是将结果输出到XML文档中,便于我们生成报告。第二种仅仅是每一次运行结束之后,在standard output中显示,不能保留测试结果数据。
我们可以将前两种输出结合起来,自己测试的时候,使用Basic模式,生成报告的时候,使用Automated模式。
注:在Curse输出模式下需要联接 -lncurses
3.2、输出方式对应的接口函数
模式 | 使用的接口函数 |
Basic | #include “CUnit/Basic.h” CU_basic_set_mode(CU_BRM_VERBOSE); CU_basic_run_tests(); |
Automated | #include “CUnit/Automated.h” CU_list_tests_to_file(); CU_automated_run_tests(); |
Console | #include “CUnit/Console.h” CU_console_run_tests(); |
Curses | #include “CUnit/CUCurses.h” CU_curses_run_tests(); |
配置使用Basic模式,运行的结果如下。
配置使用Automated模式,重新编译运行后会生成CUnitAutomated-Listing.xml和CUnitAutomated-Results.xml两个文件。文件名是在不设定的情况下,使用默认文件名。
把项目生成的CUnitAutomated-Listing.xml和CUnitAutomated-Results.xml,与CUnit安装目录下的CUnit-List.dtd、CUnit-List.xsl、CUnit-Run.dtd和CUnit-Run.xsl,共六个文件,放到一个文件夹下,拷贝到window系统下,使用IE浏览器打开。
注意,要用IE浏览器进行打开,谷歌浏览器和狐火浏览器都无法正确打开。
TestMax-Results.xml
TestMax-Listing.xml
3.3、测试函数的书写
1) 首先针对被测试的函数书写测试函数。
2) 初始化一个Registry。
3) 将特定的Suite加入到这个Registry中。可以为Suite指定初始化函数和清理函数。
4) 将测试函数加入到一个Suite中,这样,如果该测试函数的运行需要一些初始化条件,那么可以可以将代码加入到Suite的初始化函数中,当然不要忘记最后还要做对应的清理操作。
5) 使用相应的接口将测试结果输出。
6) 最后,清理Registry。
3.4、CUnit 断言
CUnit提供了大量的预定义的断言,针对几乎所有的C的标准类型,我们可以针对具体的需要检查的参数的类型进行选择。
CUnit 对断言定义如下:
断言 | 描述 |
---|---|
CU_ASSERT (int expression) CU_ASSERT_FATAL (int expression) CU_TEST (int expression) CU_TEST_FATAL (int expression) | 断言表达 expression 是 CU_TRUE(非零) |
CU_ASSERT_TRUE (value) CU_ASSERT_TRUE_FATAL (value) | 断言值 value 是 CU_TRUE(非零) |
CU_ASSERT_FALSE (value) CU_ASSERT_FALSE_FATAL (value) | 断言值 value 是 CU_FALSE(零) |
CU_ASSERT_EQUAL (actual, expected) CU_ASSERT_EQUAL_FATAL (actual, expected) | 断言 actual (实际) == expected (预期) |
CU_ASSERT_NOT_EQUAL(actual, expected) CU_ASSERT_NOT_EQUAL_FATAL (actual,expected) | 断言 actual (实际) != expected(预期) |
CU_ASSERT_PTR_EQUAL (actual, expected) CU_ASSERT_PTR_EQUAL_FATAL (actual,expected) | 断言 指针 actual(实际) == expected (预期) |
CU_ASSERT_PTR_NOT_EQUAL (actual,expected) CU_ASSERT_PTR_NOT_EQUAL_FATAL (actual, expected) | 断言 指针 actual(实际) != expected (预期) |
CU_ASSERT_PTR_NULL (value) CU_ASSERT_PTR_NULL_FATAL (value) | 断言 指针 value == NULL |
CU_ASSERT_PTR_NOT_NULL (value) CU_ASSERT_PTR_NOT_NULL_FATAL (value) | 断言 指针 value != NULL |
CU_ASSERT_STRING_EQUAL (actual,expected) CU_ASSERT_STRING_EQUAL_FATAL (actual, expected) | 断言的实际和预期的字符串是等价的 strcmp |
CU_ASSERT_STRING_NOT_EQUAL (actual,expected) CU_ASSERT_STRING_NOT_EQUAL_FATAL (actual, expected) | 断言字符串的实际和预期不同 |
CU_ASSERT_NSTRING_EQUAL (actual,expected, count) CU_ASSERT_NSTRING_EQUAL_FATAL (actual, expected, count) | 断言的实际和预期的前count个字符是相同的 strncmp |
CU_ASSERT_NSTRING_NOT_EQUAL (actual,expected, count) CU_ASSERT_NSTRING_NOT_EQUAL_FATAL (actual, expected, count) | 断言前count个字符的实际和预期的不同 |
CU_ASSERT_DOUBLE_EQUAL (actual,expected, granularity) CU_ASSERT_DOUBLE_EQUAL_FATAL (actual, expected, granularity) | Assert that fabs(actual - expected) <= fabs(granularity) Math library must be linked in for this assertion |
CU_ASSERT_DOUBLE_NOT_EQUAL (actual,expected, granularity) CU_ASSERT_DOUBLE_NOT_EQUAL_FATAL (actual, expected, granularity) | Assert that fabs(actual - expected) >fabs(granularity) Math library must be linked in for this assertion |
CU_PASS (message) | 注册一个指定的通过断言的消息。不可进行逻辑测试。 |
CU_FAIL (message) CU_FAIL_FATAL (message) | 注册一个指定的失败断言的消息。不可进行逻辑测试。 |
可以看到,每一种类型的断言都有FATAL和非FATAL两个函数,他们的区别是,非FATAL断言失败的时候,程序继续运行,而FATAL类型的断言失败之后,程序马上中止。通常我们使用非FATAL就可以了。
注意在我们的程序中,需要对返回值,out参数进行判断,对于是否是预想结果的判断的时候,一定要使用断言,而不要使用printf等等函数,一方面printf缺乏灵活性,另一方面,只有使用断言,结果报告中才会有对应的输出项。
CU_PASS,CU_FAIL这两个断言比较特殊,它们仅仅表示测试程序运行到了这个地方。比如,在某些测试函数中,被测试的函数没有任何返回值等,我们为了证明这个函数已经被运行到了,我们使用以上两个函数。它们仅仅打印出一条消息,代表执行过。还有就是,它们也被输出。
4、CUnit使用实例
strformat.c
/* Commentary:
* 此代码仅为体验Cunit而临时撰写。
*/
/* 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 */
strformat.h
/* Commentary:
* 为的是体验Cunit而临时写的几项功能函数,没有多大实际意义,仅仅是为了写测试类
*/
/* 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 */
testcase.c
/* Commentary:
* 当前文件用来定义测试方法,suite,及registry信息,若测试方法有变化,只需要修改当前文件即可。
* 第一步:书写测试函数的代码,建议以"test_"为前缀。
* 第二步:将测试方法归类,即将相似功能的测试方法放到一个数组里,以便把它们指定给一个suite
* 第三步:创建suite,可按功能或模块,生成多个test suite,
* 第四步:书写测试方法的总调用方法,AddTests(),用来统一启动测试方法。
*/
/* 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 <CUCurses.h>
#include <CUnit/CUnit.h>
#include <CUnit/TestDB.h>
#include "strformat.h"
#define BASIC 0
#define AUTOMATED 1
#define CONSOLE 0
#define CURSES 0
/**************************************************************************
函数名称:测试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[10] = "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,NULL,NULL,testcase},
{"testsuite2",suite_success_init,suite_success_clean,NULL,NULL,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();
#if (BASIC == 1)
/***** Basice Mode ****************/
CU_basic_set_mode(CU_BRM_VERBOSE);
CU_basic_run_tests();
#endif
#if (AUTOMATED == 1)
/**** Automated Mode **************/
//CU_set_output_filename("TestMax");
CU_list_tests_to_file();
CU_automated_run_tests();
#endif
#if (CONSOLE == 1)
/*****Console Mode ****************/
CU_console_run_tests();
#endif
#if (CURSES == 1)
CU_curses_run_tests();
#endif
CU_cleanup_registry();
return CU_get_error();
}
}
/*************************************************************************
*功能描述:测试类主方法
*参数列表:
*返回类型:
**************************************************************************/
int main(int argc, char * argv[])
{
return RunTest();
}
/* testcase.c ends here */
makefile:
INC=-I …/CUnit/unittest/include/
LIB=-L …/CUnit/unittest/lib/
LDLIBS += -lncurses
CFLAGS= -g -Wall
EXE= test
all: strformat.c testcase.c
gcc -o test $(INC) $(LIB) $(CFLAGS) $(LDLIBS) $^ -lcunit -static.PHONY:all clean
clean:
-@rm -f *.o $(EXE)
其中…为本地代码路径,需要根据具体情况修改。