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被组织成一个常见的单元测试框架,其结构如下:
Test Registry
|
------------------------------
| |
Suite '1' . . . . Suite 'N'
| |
--------------- ---------------
| | | |
Test '11' ... Test '1M' Test 'N1' ... Test 'NM'
测试用例被打包成测试包,并被注册到当前活动的测试注册簿中。测试包的装载和卸载函数在测试执行前后被自动调用。所有的测试包和测试用例可以一键运行,也可以选择相应的测试包或测试用例来执行测试。
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: |
2. 编写CUnit测试用例
2.1 测试用例函数的命名
CUnit中对于测试函数的定义没有严格的规范,一个常用的示例如下:
int maxi(int i1, int i2) |
2.2 CUnit中的断言
CUnit为逻辑条件测试提供了一系列的断言。测试框架会跟踪这些断言的通过或失败,当测试执行完成时便可看到结果。
每一个断言测试一个逻辑条件,条件的值为CU_FALSE表示断言失败。对于测试失败,测试将会继续执行,除非用户选择“xxx_FATAL”类型的断言,这种情况下该测试函数将会失败并立即返回。FATAL类型的断言应该和警告一块使用!一旦FATAL类型的断言导致测试失败,测试函数将没有机会做清理工作,普通的清理函数不会起任何作用。
另外一些特殊的断言被注册为“pass”或“fail”,它们不是用来做逻辑测试,而是用来测试流程控制或者其他条件测试的。例如:
void test_longjmp(void) |
所有的断言被定义在<CUnit/CUnit.h>
3. 测试注册簿
3.1 常用相关函数
#include <CUnit/TestDB.h> |
3.2 注册簿内部结构体
测试注册簿是测试包和相关测试用例的仓库。当用户添加测试包或测试用例时,CUnit维护当前活动的测试注册簿的状态更新,当用户选择运行所有测试用例时,当前活动的注册簿中所有的测试包均被执行。
测试注册簿结构在<CUnit_TestDB.h>中定义,它包括所有测试包的数量、所有测试用例的数量以及一个指向该注册簿中测试包链表的指针:
typedef struct CU_TestRegistry |
用户通常只需在使用前初始化测试注册簿,之后做清除工作即可。此外CUnit还提供了一些必要的注册簿操作函数。
3.5 与注册簿相关的其它函数
CU_pTestRegistry CU_get_registry(void) |
4. 测试包及测试用例的管理
4.1 相关函数及结构
#include <CUnit/TestDB.h> |
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[] = { |
同样的,CU_SuiteInfo也提供类似的封装功能,它将测试包名字、测试包初始化函数、清理函数和其关联的测试用例封装起来。
CU_SuiteInfo suites[] = { |
这样,我们将整个注册流程简化为:
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) |
4.7 测试包和测试用例的查询
大多数情况下,客户端可以通过注册测试包和关联测试用例获取它们的引用。有时候客户端可能需要有能力去检索某个测试包或测试用例的引用。CUnit提供给客户端获取某个测试包或测试用例信息的能力。
CU_pSuite CU_get_suite(const char* strName) |
这些函数使用户查询注册到当前活动注册簿中的测试包。可以通过传入名字、位置参数来获取测试包,如果该测试包不存在,则返回NULL。位置参数从1开始到注册簿中的测试包数。按名字查询的方式只返回测试包链表中的第一个测试包。如果注册簿没有初始化则错误码为CUE_NOREGISTRY,相应的,如果按名字查找的包不存在,错误码为CUE_NO_SUITENAME且返回NULL。
CU_pTest CU_get_test(CU_pSuite pSuite, const char *strName) |
如上函数和测试包查询类似。
5. 运行测试
5.1 常用相关函数
#include <CUnit/Automated.h> |
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。