求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
  
 
 
     
   
分享到
单元测试工具 CUnit 简介
 
火龙果软件    发布于 2014-04-15
 

1.CUnit简介

1.1 CUnit简要描述

CUnit是一个编写、管理及运行c语言单元测试的系统。它使用一个简单的框架来构建测试结构,并为普通数据结构的测试提供丰富的断言。此外,CUnit为测试的运行和结果查看提供了许多不同的接口,包括自动测试模式和可交互的控制台模式。

其常用的数据类型和函数在以下头文件中声明:

头文件 内容描述

<CUnit/CUnit.h> 包括测试用例中常用的宏定义和框架中其它头文件

<CUnit/CUError.h> 错误处理函数及错误编号

<CUnit/TestDB.h> 测试注册簿、测试包和测试用例的操作及数据类型

<CUnit/TestRun.h> 测试运行及结果检索的操作及数据类型

<CUnit/Automated.h> 输出Xml结果相关的自动模式接口

<CUnit/Basic.h> 非交互模式的基本模式接口

<CUnit/Console.h> 交互模式的接口

1.2 测试框架结构

CUnit核心框架为测试注册簿、测试包和测试用例的管理提供了基本支持,它提供的接口可以使用户和测试框架交互,方便测试的运行和测试结果的查看。CUnit被组织成一个常见的单元测试框架,其结构如下:

测试用例被打包成测试包,并被注册到当前活动的测试注册簿中。测试包的装载和卸载函数在测试执行前后被自动调用。所有的测试包和测试用例可以一键运行,也可以选择相应的测试包或测试用例来执行测试。

1.3 基本使用方法

使用CUnit框架的常用流程如下:

编写测试用例,如果有必要须对测试包进行初始化或者清理

初始化测试注册簿 CU_initialize_registry()

向注册簿中注册测试包 CU_add_suite()

向测试包中添加测试用例 CU_add_test()

使用合适的测试模式执行测试CU_automated(basic/console/curses)_run_tests()

清理测试注册簿 CU_cleanup_registry()

1.4 Linux下CUnit的安装

The usual sequence of steps should succeed in building and installing CUnit:
aclocal (if necessary)
autoconf (if necessary)
automake (if necessary)
chmod u+x configure (if necessary)
./configure --prefix <Your choice of directory for installation>
make
make install
What's installed:
libcunit.a (Library file)
CUnit Header files
DTD and XSL files supporting xml output files in share directory
Man Pages in relevant man directories under the installation path.
HTML users guide in the doc subdirectory of the installation path.
Example & test programs in the share subdirectory of the install path.

2. 编写CUnit测试用例

2.1 测试用例函数的命名

CUnit中对于测试函数的定义没有严格的规范,一个常用的示例如下:

int maxi(int i1, int i2)
  {
  return (i1 > i2) i1 : i2;
  }
  void test_maxi(void)
  {
  CU_ASSERT(maxi(0,2) == 2);
  CU_ASSERT(maxi(0,-2) == 0);
  CU_ASSERT(maxi(2,2) == 2);
  }

2.2 CUnit中的断言

CUnit为逻辑条件测试提供了一系列的断言。测试框架会跟踪这些断言的通过或失败,当测试执行完成时便可看到结果。

每一个断言测试一个逻辑条件,条件的值为CU_FALSE表示断言失败。对于测试失败,测试将会继续执行,除非用户选择“xxx_FATAL”类型的断言,这种情况下该测试函数将会失败并立即返回。FATAL类型的断言应该和警告一块使用!一旦FATAL类型的断言导致测试失败,测试函数将没有机会做清理工作,普通的清理函数不会起任何作用。

另外一些特殊的断言被注册为“pass”或“fail”,它们不是用来做逻辑测试,而是用来测试流程控制或者其他条件测试的。例如:

void test_longjmp(void)
{
jmp_buf buf;
int i;
i = setjmp(buf);
if (i == 0) {
run_other_func();
CU_PASS("run_other_func() succeeded.");
}
else
CU_FAIL("run_other_func() issued longjmp.");
}

所有的断言被定义在<CUnit/CUnit.h>

3. 测试注册簿

3.1 常用相关函数

#include  <CUnit/TestDB.h>
typedef struct CU_TestRegistry
typedef CU_TestRegistry* CU_pTestRegistry
CU_ErrorCode CU_initialize_registry(void)
void CU_cleanup_registry(void)
CU_BOOL CU_registry_initialized(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.2 注册簿内部结构体

测试注册簿是测试包和相关测试用例的仓库。当用户添加测试包或测试用例时,CUnit维护当前活动的测试注册簿的状态更新,当用户选择运行所有测试用例时,当前活动的注册簿中所有的测试包均被执行。

测试注册簿结构在<CUnit_TestDB.h>中定义,它包括所有测试包的数量、所有测试用例的数量以及一个指向该注册簿中测试包链表的指针:

typedef struct CU_TestRegistry
{
unsigned int uiNumberOfSuites;
unsigned int uiNumberOfTests;
CU_pSuite pSuite;
} CU_TestRegistry;
typedef CU_TestRegistry* CU_pTestRegistry;

3.3 与注册簿相关的其它函数

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)

4. 测试包及测试用例的管理

4.1 相关函数及结构

#include <CUnit/TestDB.h>
typedef struct CU_Suite
typedef CU_Suite* CU_pSuite
typedef struct CU_Test
typedef CU_Test* CU_pTest
typedef void (*CU_TestFunc)(void)
typedef int (*CU_InitializeFunc)(void)
typedef int (*CU_CleanupFunc)(void)
CU_pSuite CU_add_suite(const char* strName,CU_InitializeFunc pInit,CU_CleanupFunc pClean);
CU_pTest CU_add_test(CU_pSuite pSuite,const char* strName,CU_TestFunc pTestFunc);
typedef struct CU_TestInfo
typedef struct CU_SuiteInfo
CU_ErrorCode CU_register_suites(CU_SuiteInfo suite_info[]);
CU_ErrorCode CU_register_nsuites(int suite_count, ...);
CU_ErrorCode CU_set_suite_active(CU_pSuite pSuite, CU_BOOL fNewActive)
CU_ErrorCode CU_set_test_active(CU_pTest, CU_BOOL fNewActive)
CU_ErrorCode CU_set_suite_name(CU_pSuite pSuite, const char *strNewName)
CU_ErrorCode CU_set_suite_initfunc(CU_pSuite pSuite, CU_InitializeFunc pNewInit)
CU_ErrorCode CU_set_suite_cleanupfunc(CU_pSuite pSuite, CU_CleanupFunc pNewClean)
CU_ErrorCode CU_set_test_name(CU_pTest pTest, const char *strNewName)
CU_ErrorCode CU_set_test_func(CU_pTest pTest, CU_TestFunc pNewFunc)
CU_pSuite CU_get_suite(const char* strName)
CU_pSuite CU_get_suite_at_pos(unsigned int pos)
unsigned int CU_get_suite_pos(CU_pSuite pSuite)
unsigned int CU_get_suite_pos_by_name(const char* strName)
CU_pTest CU_get_test(CU_pSuite pSuite, const char *strName)
CU_pTest CU_get_test_at_pos(CU_pSuite pSuite, unsigned int pos)
unsigned int CU_get_test_pos(CU_pSuite pSuite, CU_pTest pTest)
unsigned int CU_get_test_pos_by_name(CU_pSuite pSuite, const char *strName)

4.2 注册测试包

CU_pSuite CU_add_suite(const char* strName, CU_InitializeFunc    pInit, CU_CleanupFunc pClean)

创建一个测试包,该测试包拥有自己特定的名字、初始化函数及清理函数。该测试包被注册到一个测试注册簿,该注册簿在添加任意测试包之前须初始化。当前版本不支持独立于注册簿之外的测试包的创建,该函数不应该在测试执行期间被调用。

在注册簿中,推荐每个测试包有唯一的名字,这样可以通过名字查找测试包。在上述函数中,测试包的初始化函数和清理函数是可选的,如果不需要这些函数可以传参数NULL。

该函数返回值分为五种:

CUE_SUCCESS suite creation was successful.

  CUE_NOREGISTRY Error the registry has not been initialized.

  CUE_NO_SUITENAME ErrorstrName was

  NULL.CUE_DUP_SUITE Warning the suite's name was not unique.

  CUE_NOMEMORY Error memory allocation failed.

4.3 添加测试用例到测试包

CU_pTest CU_add_test(CU_pSuite pSuite, const char* strName, CU_TestFunc pTestFunc)

创建一个测试用例,该测试包拥有自己特定的名字、初始化函数及清理函数。该测试用例被打包到一个测试包,当前版本不支持独立于测试包之外的创建,该函数不应该在测试执行期间被调用。

在单个测试包中,推荐每个测试用例有唯一的名字,这样可以通过名字查找测试用例。参数接受一个测试函数的函数指针,不可以为空,当执行测试时,该函数将被调用。测试函数没有参数也没有返回值。

该函数返回值分为7种:

CUE_SUCCESS suite creation was successful.

  CUE_NOREGISTRY Error: the registry has not been initialized.

  CUE_NOSUITE Error: the specified suite was NULL or invalid.

  CUE_NO_TESTNAME Error: strName was NULL.

  CUE_NO_TEST Error: pTestFunc was NULL or invalid.

  CUE_DUP_TEST Warning: the test's name was not unique.

  CUE_NOMEMORY Error: memory allocation failed.

4.4 测试包及测试用例管理的快捷方法

CUnit定义了许多类似如下的宏:

#define CU_ADD_TEST(suite, test) (CU_add_test(suite, #test, (CU_TestFunc)test))

这些宏可以针对测试函数名字,自动生成拥有惟一名字的测试用例,并将该测试用例添加到指定的测试包,用户应该验证返回值以保证正常添加。

CU_ErrorCode CU_register_suites(CU_SuiteInfo suite_info[])

  CU_ErrorCode CU_register_nsuites(int suite_count, ...)

对于拥有很多测试包和测试用例的大型测试结构,管理测试包和测试用例的关联和注册是相当乏味和易出错的。CUnit提供了一个特殊的注册系统来帮助用户管理测试包和测试用例。这个系统将测试包的注册和测试用例的关联集中起来,以缩减用户的代码量。

CU_TestInfo实例可以将许多测试用例集中放到一个数组,以便于关联到一个测试包。每个数组元素包括一个惟一的名字和测试函数。该数组必须以CU_TEST_INFO_NULL结尾。

CU_TestInfo test_array1[] = {
{ "testname1", test_func1 },
{ "testname2", test_func2 },
{ "testname3", test_func3 },
CU_TEST_INFO_NULL,
};

同样的,CU_SuiteInfo也提供类似的封装功能,它将测试包名字、测试包初始化函数、清理函数和其关联的测试用例封装起来。

CU_SuiteInfo suites[] = {
{ "suitename1", suite1_init-func, suite1_cleanup_func, test_array1 },
{ "suitename2", suite2_init-func, suite2_cleanup_func, test_array2 },
CU_SUITE_INFO_NULL,
};

这样,我们将整个注册流程简化为:

CU_ErrorCode error = CU_register_suites(suites);

或者

CU_ErrorCode error = CU_register_nsuites(2, suites1, suites2);

这些函数的返回值和包注册函数、测试用例关联函数相同

4.5 设置当前活动测试包和测试用例

CU_ErrorCode CU_set_suite_active(CU_pSuite pSuite, CU_BOOL fNewActive)

  CU_ErrorCode CU_set_test_active(CU_pTest pTest, CU_BOOL fNewActive)

这些函数被用来测试包测试用例为当前活动包和当前活动测试用例,一个测试包或者测试用例在执行测试时不会被执行,除非用户将它设置为是当前活动的。所有的测试包和测试用例在创建时被默认设置为活动的。当前活动状态可以通过pSuite->fActive或pTest->fActive获取。这样,客户端就有能力动态地选择测试用例去执行测试。如果参数对应的包或者用例不存在则返回CUE_NOSUIT或CUI_NOTEST。

4.6 修改测试包和测试用例的属性

CU_ErrorCode CU_set_suite_name(CU_pSuite pSuite, const char *strNewName)
CU_ErrorCode CU_set_test_name(CU_pTest pTest, const char *strNewName)
CU_ErrorCode CU_set_suite_initfunc(CU_pSuite pSuite, CU_InitializeFunc pNewInit)
CU_ErrorCode CU_set_suite_cleanupfunc(CU_pSuite pSuite, CU_CleanupFunc pNewClean)
CU_ErrorCode CU_set_test_func(CU_pTest pTest, CU_TestFunc pNewFunc)

4.7 测试包和测试用例的查询

大多数情况下,客户端可以通过注册测试包和关联测试用例获取它们的引用。有时候客户端可能需要有能力去检索某个测试包或测试用例的引用。CUnit提供给客户端获取某个测试包或测试用例信息的能力。

CU_pSuite CU_get_suite(const char* strName)
CU_pSuite CU_get_suite_at_pos(unsigned int pos)
unsigned int CU_get_suite_pos(CU_pSuite pSuite)
unsigned int CU_get_suite_pos_by_name(const char* strName)

这些函数使用户查询注册到当前活动注册簿中的测试包。可以通过传入名字、位置参数来获取测试包,如果该测试包不存在,则返回NULL。位置参数从1开始到注册簿中的测试包数。按名字查询的方式只返回测试包链表中的第一个测试包。如果注册簿没有初始化则错误码为CUE_NOREGISTRY,相应的,如果按名字查找的包不存在,错误码为CUE_NO_SUITENAME且返回NULL。

CU_pTest CU_get_test(CU_pSuite pSuite, const char *strName)
CU_pTest CU_get_test_at_pos(CU_pSuite pSuite, unsigned int pos)
unsigned int CU_get_test_pos(CU_pSuite pSuite, CU_pTest pTest)
unsigned int CU_get_test_pos_by_name(CU_pSuite pSuite, const char *strName)

如上函数和测试包查询类似。

5. 运行测试

5.1 常用相关函数

#include 
void         CU_automated_run_tests(void)
CU_ErrorCode CU_list_tests_to_file(void)
void         CU_set_output_filename(const char* szFilenameRoot)
#include 
typedef enum     CU_BasicRunMode
CU_ErrorCode     CU_basic_run_tests(void)
CU_ErrorCode     CU_basic_run_suite(CU_pSuite pSuite)
CU_ErrorCode     CU_basic_run_test(CU_pSuite pSuite, CU_pTest pTest)
void             CU_basic_set_mode(CU_BasicRunMode mode)
CU_BasicRunMode CU_basic_get_mode(void)
void             CU_basic_show_failures(CU_pFailureRecord pFailure)
#include 
void CU_console_run_tests(void)
#include 
void CU_curses_run_tests(void)
#include 
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)
typedef struct CU_RunSummary
typedef CU_Runsummary* CU_pRunSummary
const CU_pRunSummary CU_get_run_summary(void)
typedef struct CU_FailureRecord
typedef CU_FailureRecord*  CU_pFailureRecord
const CU_pFailureRecord CU_get_failure_list(void)
unsigned int CU_get_number_of_failure_records(void)
void CU_set_fail_on_inactive(CU_BOOL new_inactive)
CU_BOOL CU_get_fail_on_inactive(void)

5.2 自动模式

CUnit支持运行注册簿中所有的测试用例,它同时支持单独运行某个测试包或测试用例。 CUnit框架会在每个测试运行期间跟踪测试包、用例、断言以及断言通过和失败的数量。需要注意的是,每次测试初始化(即便是初始化失败)前次的测试结果都会被清空。,如果客户端想排除某些用例以做某个特殊测试,单个测试包或测试用例可以被设置为非活动。

自动模式接口提供非交互模式测试,用户初始化测试并运行,结果被导出到一个XML文件,所有的测试注册簿和测试包均可以被导出到XML文件。自动模式接口包括如下函数:

void CU_automated_run_tests(void) 该函数运行注册簿中所有活动的的测试包,测试结果被输出到一个名字为ROOT-Results的XML文件。ROOT可以通过 CU_set_output_filename()设置,否则使用默认文件名 CUnitAutomated-Results.xml。需要指出的是,如果不设置一个独特的名字,测试结果会被覆盖。

CU_ErrorCode CU_list_tests_to_file(void) 该函数在文件中列出所有注册的测试包及相关联的测试用例。列表文件名为ROOT-Listing.XML。名字ROOT可以通过 CU_set_output_filename()设置,否则默认文件名CUnitAutomated便被启用,同样的,如果不区分名字,该列表文件将会被覆盖。需要指出的是,如果用户需要一个列表文件,他必须显示地去调用该接口函数。

void CU_set_output_filename(const char* szFilenameRoot) 这个函数用于设置输出结果或列表文件的文件名,该参数后面会相应的追加-Results.xml或-Listing.xml。

相关文章

微服务测试之单元测试
一篇图文带你了解白盒测试用例设计方法
全面的质量保障体系之回归测试策略
人工智能自动化测试探索
相关文档

自动化接口测试实践之路
jenkins持续集成测试
性能测试诊断分析与优化
性能测试实例
相关课程

持续集成测试最佳实践
自动化测试体系建设与最佳实践
测试架构的构建与应用实践
DevOps时代的测试技术与最佳实践
 
分享到
 
 
     


LoadRunner性能测试基础
软件测试结果分析和质量报告
面向对象软件测试技术研究
设计测试用例的四条原则
功能测试中故障模型的建立
性能测试综述
更多...   


性能测试方法与技术
测试过程与团队管理
LoadRunner进行性能测试
WEB应用的软件测试
手机软件测试
白盒测试方法与技术


某博彩行业 数据库自动化测试
IT服务商 Web安全测试
IT服务商 自动化测试框架
海航股份 单元测试、重构
测试需求分析与测试用例分析
互联网web测试方法与实践
基于Selenium的Web自动化测试
更多...