C语言单元测试框架——CUnit入门

1、CUnit简介

CUnit 是一个通过 C 语言编写的用于编写、管理和执行单元测试用例的C语言单元测试框架。

它往往被编成库的形式(静态库或动态库)提供给用户测试代码进行使用,用户编写程序的时候直接链接此静态库就可以了。

它提供了一个简单的单元测试框架,并且为常用的数据类型提供了丰富的断言语句支持。
CUnit是平台无关的框架与各种用户接口的组合。核心框架为管理测试注册表,套件和测试用例提供了基本支持。用户接口便于与框架交互以运行测试和查看结果。CUnit的组织结构与传统的单元测试框架类似:


单独的测试用例(Test)被打包到套件(Suite)中,这些套件又被注册到活动测试注册表(Test Registry)里。每个套件都有自己的构造和析构函数,这两个函数将在运行套件测试之前和之后被自动调用。注册表中的所有套件/测试用例,可以通过调用一个函数执行全部测试,也可以有选择性地执行部分测试。

2、CUnit安装

CUnit的首页

CUnit 的文档手册地址

中文翻译版

CUnit 2-1-3下载地址

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、输出方式

接口

平台描述
AutomatedAll非交互式,输出XML文件
BasicAll非交互式,可选输出到stdout
ConsoleAll控制台交互方式,在用户控制下运行
CursesLinux/UnixCurses交互方式(图形界面),在用户控制下运行

如上边所示,后两种主要是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)

其中…为本地代码路径,需要根据具体情况修改。

  • 21
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小朋友的大哈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值