CUnit详解(基于linux下的白盒单元测试)

CUnit是一个对C语言编写的程序进行单元测试的框架,在线文档说它作为一个静态链接库被链接到用户的测试代码中。

它提供了一种简洁的框架来建立测试架构,并提供丰富的断言(Assertion)来测试通用数据类型。除此之外,它还提供了

许多不同的结构来运行测试用例和报告测试结果。

(1)CUnit的架构

可以看出Cunit也是有组织的,主要分几个角色,Registry,Suite及Test方法。可以通过下面例子,体会到这种组织关系。

按官方文档说明,使用Cunit的主要步骤有:

1) Write functions for tests (and suite init/cleanup if necessary).

2) Initialize the test registry - CU_initialize_registry()

3) Add suites to the test registry - CU_add_suite()

4) Add tests to the suites - CU_add_test()

5) Run tests using an appropriate interface, e.g. CU_console_run_tests

6) Cleanup the test registry - CU_cleanup_registry

(2)测试模式

下面是四种测试模式:

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)     Interactive

第一种模式是将结果输出到XML文档中,便于生成报告。第二种模式是每一次运行结束之后在standard output中显示测试结果,不能保留测试结果数据。第三种模式是console方式的,可以人机交互;前两种模式是非交互式的。第四种只在Unix中使用。

(3)测试的基本流程

1)编写单元测试函数(有必要的话要写suite的init/cleanup函数)。Write functions for tests (and suite init/cleanup if necessary).

2)调用函数CU_initialize_registry()初始化测试注册单元(Test Registry)。 Initialize the test registry - CU_initialize_registry()

3)调用函数CU_add_suite() 将测试包(suite)添加到测试注册单元(Test Registry)中。Add suites to the test registry - CU_add_suite()

4)调用函数CU_add_test()将测试用例添加到测试包(suite)中。Add tests to the suites - CU_add_test()

5)使用合适的接口来运行测试用例。Run tests using an appropriate interface, e.g. CU_console_run_tests

6)调用函数CU_cleanup_registry清除测试注册单元(Test Registry)。Cleanup the test registry - CU_cleanup_registry()

先编写一个具体两个简单功能的函数,然后写Testcase来测试它。

文件主要有:

1) strformat.h :字符串功能函数的接口文件

2)strformat.c :字符串功能函数的实现

3)testcase.c : 测试用例及Cunit运行环境

4)makefile :

直接上代码吧,strformat.h

#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.c

#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;
}

testcase.c

/* testcase.c ---
 *
 * Filename: testcase.c
 * Description: 测试实例
 */

/* 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 <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();

}

代码:makefile

IINC=-I/usr/local/include/CUnit
LIB=-L/usr/local/lib/

all:  strformat.c testcase.c
    gcc -o test $(INC) $(LIB)  $^ -lcunit -static

注:

1)Cunit安装很简单,从官方地址上下载源代码后,在本机依次执行

./configure

make

sudo make install

安装成功后相关的库及头文件安装到默认路径下。编译时添加选项:

-I/usr/local/include/CUnit

-L/usr/local/lib/

就如makefile中的一样。

下面我们欣赏一下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模式,这种是不支持交互的,直接打印出运行结果。

可以看出,这种模式也是比较简单快捷的

2.在两种不同环境下使用CUnit来上面的例子进行单元测试。

2.1 在Windows XP下,在VC 6.0 集成CUnit来进行单元测试。

(1)自己动手生成链接静态库CUnit.lib。

下载CUnit-2.1-0-src.zip,解压,在CUnit-2.1-0/CUnit目录下,用VC 6.0打开工程文件CUnit.dsp,有个提示,说没有找到workspace文件,不会理会,直接确定。然后在当前打开的工程项目中,点击"FileView",展开文件树形结构,任意点击一个文件打开,然后在最上面的工具栏点击“编译”和“链接”,在CUnit-2.1-0/CUnit目录下就生产CUnit.lib。

(2)在VC 6.0新建一个名为"Test_CUit"的Win32 console application工程,将上面例子中的文件包含进来。

(3)下载CUnit-2.1-0-winlib.zip,解压,把解压的目录CUnit-2.1-0复制到当前的工程目录下。

(4)将CUnit.lib叶复制到当前的工程目录下,并在"Project"->"Settings..."->"Link"->"Object/library modules"中内容的末尾增加CUnit.lib,用空格和前面的静态库文件隔开,然后点击"OK"即可。

(5)在"Test_CUit"工程项目中,在最上面的工具栏点击“编译”->“链接”->“运行”,就可以看到控制台界面。输入相应的操作符号进行对应的操作,不再详述。

备注:在TestMainModule.c的AddTestMainModule()函数中注释掉的部分是第一种测试模式"Automated Output to xml file",默认的模式是第三种控制台模式。把控制台模式注释掉,而把第一种测试模式打开,则运行时会在当前目录下三个XML文件:CUnit-Memory-Dump.xml,TestMax-Listing.xml,TestMax-Results.xml。将这三个文件复制到目录CUnit-2.1-0/share/CUnit下,然后用浏览器打开后两个XML文件,可以看到测试报告的结果。

2.2 在Linux下,利用CUnit来进行单元测试。

1.编译CUnit,编译后,头文件目录在/root/local/include/CUnit中,静态库文件在/root/local/lib/下。

(1)用root用户登录,下载CUnit-2.1-0-src.tar.gz。

(2)tar -zxvf CUnit-2.1-0-src.tar.gz

(3)cd CUnit-2.1-0

(4)./configure --prefix=$HOME/local

(5)make

(6)make install

2.编写一个Makefile文件,放入到在源文件目录中,内容如下:

  1. INC=-I/root/local/include
  2. LIB=-L/root/local/lib
  3. all: MainModule.c TestMainModule.c CUnitRunTest.c
  4. gcc $^ -o test $(INC) $(LIB) -lcunit -lcurses -static
  5. clean:
  6. rm -rf test

3.在源文件目录下执行make命令即可。

4.运行./test即可看见console命令界面。

3.关于断言的使用

CUnit提供了一系列的断言来测试逻辑条件,这些断言的成功或是失败的结果都由CUnit单元测试框架跟踪,并在单元测试结束后可以看到。每个断言测试一个逻辑条件。如果选择"XXX_FATAL"这些断言,意味着断言失败后单元测试用例不再继续执行,所以带"FATAL"的断言版本需要谨慎使用。断言被包含在"CUnit/CUnit.h"这个头文件中声明的。现在就从CUnit的文档说明中摘录它的断言声明。

CU_ASSERT(int expression)

CU_ASSERT_FATAL(int expression)

CU_TEST(int expression)

CU_TEST_FATAL(int expression)

Assert that expression is TRUE (non-zero)
CU_ASSERT_TRUE(value)

CU_ASSERT_TRUE_FATAL(value)

Assert that value is TRUE (non-zero)
CU_ASSERT_FALSE(value)

CU_ASSERT_FALSE_FATAL(value)

Assert that value is FALSE (zero)
CU_ASSERT_EQUAL(actual, expected)

CU_ASSERT_EQUAL_FATAL(actual, expected)

Assert that actual = = expected
CU_ASSERT_NOT_EQUAL(actual, expected))

CU_ASSERT_NOT_EQUAL_FATAL(actual, expected)

Assert that actual != expected
CU_ASSERT_PTR_EQUAL(actual, expected)

CU_ASSERT_PTR_EQUAL_FATAL(actual, expected)

Assert that pointers actual = = expected
CU_ASSERT_PTR_NOT_EQUAL(actual, expected)

CU_ASSERT_PTR_NOT_EQUAL_FATAL(actual, expected)

Assert that pointers actual != expected
CU_ASSERT_PTR_NULL(value)

CU_ASSERT_PTR_NULL_FATAL(value)

Assert that pointer value == NULL
CU_ASSERT_PTR_NOT_NULL(value)

CU_ASSERT_PTR_NOT_NULL_FATAL(value)

Assert that pointer value != NULL
CU_ASSERT_STRING_EQUAL(actual, expected)

CU_ASSERT_STRING_EQUAL_FATAL(actual, expected)

Assert that strings actual and expected are equivalent
CU_ASSERT_STRING_NOT_EQUAL(actual, expected)

CU_ASSERT_STRING_NOT_EQUAL_FATAL(actual, expected)

Assert that strings actual and expected differ
CU_ASSERT_NSTRING_EQUAL(actual, expected, count)

CU_ASSERT_NSTRING_EQUAL_FATAL(actual, expected, count)

Assert that 1st count chars of actual and expected are the same
CU_ASSERT_NSTRING_NOT_EQUAL(actual, expected, count)

CU_ASSERT_NSTRING_NOT_EQUAL_FATAL(actual, expected, count)

Assert that 1st count chars of actual and expected differ
CU_ASSERT_DOUBLE_EQUAL(actual, expected, granularity)

CU_ASSERT_DOUBLE_EQUAL_FATAL(actual, expected, granularity)

Assert that |actual - expected| <= |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 |actual - expected| > |granularity|

Math library must be linked in for this assertion.

CU_PASS(message) Register a passing assertion with the specified message. No logical test is performed.
CU_FAIL(message)

CU_FAIL_FATAL(message)

Register a failed assertion with the specified message. No logical test is performed

给出两个版本的源代码供下载。注:在VC6.0下面的源文件不能直接在Linux下使用,如果要用,则提示每个源文件的末尾都要加一个换行符。

【1】VC 6.0 的例子

【2】Linux的例子(无法上传.tar.gz格式的文件)

时间: 2024-10-15 12:29:35

CUnit详解(基于linux下的白盒单元测试)的相关文章

MVC之前的那点事儿系列(3):HttpRuntime详解分析(下)(转载)

MVC之前的那点事儿系列(3):HttpRuntime详解分析(下) 文章内容 话说,经过各种各样复杂的我们不知道的内部处理,非托管代码正式开始调用ISPAIRuntime的ProcessRequest方法了(ISPAIRuntime继承了IISPAIRuntime接口,该接口可以和COM进行交互,并且暴露了ProcessRequest接口方法).至于为什么要调用这个方法,大叔也不太清楚,找不到微软相关的资料哦.但大叔确定该方法就是我们进入HttpRuntime的正式大门,接着看吧. publi

JIRA WIKI 安装详解 for Linux

JIRA 安装详解 for Linux http://wiki.csdn.net/pages/viewpage.action?pageId=1441800 Confluence 安装详解 for Linux http://wiki.csdn.net/pages/viewpage.action?pageId=1867879 wiki官方教程文档 https://confluence.atlassian.com/doc/confluence-documentation-home-135922.htm

HashMap实现详解 基于JDK1.8

HashMap实现详解 基于JDK1.8 1.数据结构 散列表:是一种根据关键码值(Key value)而直接进行访问的数据结构.采用链地址法处理冲突. HashMap采用Node<K,V>数组作为散列表来存储数据.源码声明如下: /** * The table, initialized on first use, and resized as * necessary. When allocated, length is always a power of two. * (We also to

基于Linux下Nagios的安装与配置说明介绍[图]

本篇文章小编为大家介绍,基于Linux下Nagios的安装与配置说明介绍[图].需要的朋友参考下 一.Nagios简介 Nagios是一款开源的电脑系统和网络监视工具,能有效监控Windows.Linux和Unix的主机状态,交换机路由器等网络设置,打印机等.在系统或服务状态异常时发出邮件或短信报警第一时间通知网站运维人员,在状态恢复后发出正常的邮件或短信通知. Nagios原名为NetSaint,由Ethan Galstad开发并维护至今.NAGIOS是一个缩写形式: "Nagios Ain'

基于linux下的NIST数字测试(下)——测试过程

基于linux下的NIST数字测试(下)--测试过程 1.下载安装Matlab--来自微信公众号,软件安装管家 利用Matlab生成随机数x = randsrc(1,32e3,[0,1]) 把数据复制到txt文本中 然后把随机数矩阵的文本suijishu.txt复制到ubuntu 的NIST数字测试目录下 2.准备环境,进行测试 按着提示进行选择,并且输入文件名 选择测试类型 分别对应以下15种测试类型 1.频率(单比特)测试 2.块内频数测试(Frequency Test within a B

HA集群之keepalived详解/基于keepalived+LVS-DR构建HA主备模型(一)

一.理论部分:     keepalived是vrrp协议的实现:原生设计目的为高可用ipvs服务:keepalived能够配置文件中的定义生成ipvs规则:并能够对各RealServer的健康状态进行检测:  vrrp协议:虚拟冗余路由协议:早期只是主要在路由器上提供的一种非常简单的完成将多个物理设备组建成一个虚拟设备,并且在多个物理设备之间漂移地址一种协议:非常轻量化,性能非常好.而keepalived无非就是通过vrrp协议在Linux主机上通过一个守护进程,把Linux主机扮演成路由器,

Apache httpd(apache2)服务配置详解,Mac下设置虚拟主机部署多个web项目,及反向代理部署Java项目

Apache httpd服务配置详解 查看版本:httpd -v Server version: Apache/2.4.33 (Unix) Server built:   Apr  3 2018 17:54:07 文件路径:/etc/apache2/httpd.conf # 服务目录(全局配置)用于指定Apache的安装路径,# 此选项参数值在安装Apache时系统会自动把Apache的路径写入ServerRoot "/usr" # 设置互斥对象的目录# Mutex default:/

Linux进程退出详解(do_exit)--Linux进程的管理与调度(十四))

日期 内核版本 架构 作者 GitHub CSDN 2016-05-12 Linux-4.6 X86 & arm gatieme LinuxDeviceDrivers Linux进程管理与调度 Linux进程的退出 linux下进程退出的方式 正常退出 从main函数返回return 调用exit 调用_exit 异常退出 调用abort 由信号终止 _exit, exit和_Exit的区别和联系 _exit是linux系统调用,关闭所有文件描述符,然后退出进程. exit是c语言的库函数,他最

Linux进程退出详解(do_exit)--Linux进程的管理与调度(十四)

Linux进程的退出 linux下进程退出的方式 正常退出 从main函数返回return 调用exit 调用_exit 异常退出 调用abort 由信号终止 _exit, exit和_Exit的区别和联系 _exit是linux系统调用,关闭所有文件描述符,然后退出进程. exit是c语言的库函数,他最终调用_exit.在此之前,先清洗标准输出的缓存,调用用atexit注册的函数等, 在c语言的main函数中调用return就等价于调用exit. _Exit是c语言的库函数,自c99后加入,等