CUnit

简介

CUnit是一个轻量级的系统,用于对C代码进行单元测试, CUnit以静态库的方式提供给用户使用。

主要头文件如下。

CUnit/CUnit.h
主要包括用于测试的宏
CUnit/CUError.h
错误处理函数和数据类型
CUnit/TestDB.h
用于registry、suites和tests
CUnit/TestRun.h
运行测试和生成结果
CUnit/Automated.h
用于输出xml
CUnit/Basic.h
非交互式输出到stdout的接口
CUnit/Console.h
交互式console接口
CUnit/CUCurses.h
交互式console接口(*nix)
CUnit/Win.h
Windows接口

cunit-frame.png

基本用法

  • 编写测试函数,suite的init/cleanup
  • 初始化registry,CU_initialize_registry()
  • 添加suite到registry,CU_add_suite()
  • 添加test到suite,CU_add_test()
  • 运行测试,CU_console_run_tests()
  • 清除registry,CU_cleanup_registry()

CUnit使用详解

测试宏

测试函数的原型为void test_func(void),如下所示。

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

如果测试失败,将继续测试下去,如果调用FATAL版本,那么测试出错就会退出,使用FATAL版本需要慎重,因为测试函数可能没有机会执行清除动作。

基本测试宏如下所示。

CU_ASSERT(int expression)
CU_ASSERT_FATAL(int expression)
CU_TEST(int expression)
CU_TEST_FATAL(int expression)

CU_ASSERT_TRUE(value)
CU_ASSERT_TRUE_FATAL(value)
CU_ASSERT_FALSE(value)
CU_ASSERT_FALSE_FATAL(value)
CU_ASSERT_EQUAL(actual, expected)
CU_ASSERT_EQUAL_FATAL(actual, expected)
CU_ASSERT_NOT_EQUAL(actual, expected)
CU_ASSERT_NOT_EQUAL_FATAL(actual, expected)

CU_ASSERT_PTR_EQUAL(actual, expected)
CU_ASSERT_PTR_EQUAL_FATAL(actual, expected)
CU_ASSERT_PTR_NOT_EQUAL(actual, expected)
CU_ASSERT_PTR_NOT_EQUAL_FATAL(actual, expected)
CU_ASSERT_PTR_NULL(value)
CU_ASSERT_PTR_NULL_FATAL(value)
CU_ASSERT_PTR_NOT_NULL(value)
CU_ASSERT_PTR_NOT_NULL_FATAL(value)

CU_ASSERT_STRING_EQUAL(actual, expected)
CU_ASSERT_STRING_EQUAL_FATAL(actual, expected)
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)
CU_ASSERT_NSTRING_NOT_EQUAL(actual, expected, count)
CU_ASSERT_NSTRING_NOT_EQUAL_FATAL(actual, expected, count)

CU_ASSERT_DOUBLE_EQUAL(actual, expected, granularity)
CU_ASSERT_DOUBLE_EQUAL_FATAL(actual, expected, granularity)
CU_ASSERT_DOUBLE_NOT_EQUAL(actual, expected, granularity)
CU_ASSERT_DOUBLE_NOT_EQUAL_FATAL(actual, expected, granula

CU_PASS(message)
CU_FAIL(message)
CU_FAIL_FATAL(message)

Test Registry

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

CU_ErrorCode     CU_initialize_registry(void);
void             CU_cleanup_registry(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);
CU_cleanup_registry
只会影响到内部的CU_TestRegistry,用户的数据需要用户自己去维护。
CU_get_registry
获取一个CU_TestRegistry,用户不要去直接访问结构的成员。
CU_set_registry
返回的是old registry,释放old registry是用户的责任。

管理Test和Suite

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_add_suite
将suite加入到registry,因此必须要先初始化registry。如果添加成功就会返回suite的指针,否则返回NULL并设置error-code,含义如下表所示。
CUE_SUCCESS suite creation was successful.
CUE_NOREGISTRY the registry has not been initialized.
CUE_NO_SUITENAME strName was NULL.
CUE_DUP_SUITE the suite's name was not unique.
CUE_NOMEMORY memory allocation failed.
CU_add_test
成功返回test指针,失败返回NULL并设置error-code,含义如下表所示。
CUE_SUCCESS suite creation was successful.
CUE_NOSUITE the specified suite was NULL or invalid.
CUE_NO_TESTNAME strName was NULL.
CUE_NO_TEST pTestFunc was NULL or invalid.
CUE_DUP_TEST the test's name was not unique.
CUE_NOMEMORY memory allocation failed.

除此之外还定义了一组宏来简化添加操作,如下所示。

#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, ...);
CU_ADD_TEST
用于简化添加测试
CU_register_suites
用数组定义多个测试集,一次将数组内的测试集都添加进来。
CU_register_nsuites
这个函数更夸张,将多个测试集数组一次性添加进来。

我个人比较喜欢数组写法,举例如下。

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

CU_SuiteInfo suites1[] = {
    { "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(suites1);
CU_ErrorCode error = CU_register_nsuites(2, suites1, suites2);

运行测试

Automated mode
void         CU_automated_run_tests(void);
CU_ErrorCode CU_list_tests_to_file(void);
void         CU_set_output_filename(const char* szFilenameRoot);
CU_automated_run_tests
测试输出结果为ROOT-Results.xml, ROOT可以用CU_set_output_filename()设定,默认名字为CUnitAutomated-Results.xml。
CU_list_tests_to_file
将注册的测试集和关联测试输出到文件,文件名为ROOT-Listing.xml。
CU_set_output_filename
指定文件名,默认使用CUnitAutomated。
Basic mode
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);
CU_basic_run_tests
Runs all tests in all registered suites
CU_basic_run_suite
Runs all tests in single specified suite
CU_basic_run_test
Runs a single test in a specified suite
CU_basic_set_mode
设置运行模式
CU_BRM_NORMAL
输出错误信息和概要
CU_BRM_SILENT
仅输出错误信息
CU_BRM_VERBOSE
详细输出所有信息
CU_basic_get_mode
Retrieves the current basic run mode code
CU_basic_show_failures
Prints a summary of all failures to stdout. Does not depend on the run mode.
Interactive Console Mode
void CU_console_run_tests(void);
Interactive Curses Mode
void CU_curses_run_tests(void);
Getting Test Results
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
{
    unsigned int nSuitesRun;
    unsigned int nSuitesFailed;
    unsigned int nTestsRun;
    unsigned int nTestsFailed;
    unsigned int nAsserts;
    unsigned int nAssertsFailed;
    unsigned int nFailureRecords;
} CU_RunSummary;
typedef CU_Runsummary* CU_pRunSummary;
const CU_pRunSummary CU_get_run_summary(void);

typedef struct CU_FailureRecord
{
    unsigned int  uiLineNumber;
    char*         strFileName;
    char*         strCondition;
    CU_pTest      pTest;
    CU_pSuite     pSuite;

    struct CU_FailureRecord* pNext;
    struct CU_FailureRecord* pPrev;
} CU_FailureRecord;
typedef CU_FailureRecord*  CU_pFailureRecord;
const CU_pFailureRecord CU_get_failure_list(void);
unsigned int CU_get_number_of_failure_records(void);

错误处理

typedef enum CU_ErrorCode;
CU_ErrorCode   CU_get_error(void);
const char*    CU_get_error_msg(void);

typedef enum CU_ErrorAction;
void           CU_set_error_action(CU_ErrorAction action);
CU_ErrorAction CU_get_error_action(void);

错误码定义如下表所示。

CUE_SUCCESS No error condition.
CUE_NOMEMORY Memory allocation failed.
CUE_NOREGISTRY Test registry not initialized.
CUE_REGISTRY_EXISTS Attempt to CU_set_registry() without CU_cleanup_registry().
CUE_NOSUITE A required CU_pSuite pointer was NULL.
CUE_NO_SUITENAME Required CU_Suite name not provided.
CUE_SINIT_FAILED Suite initialization failed.
CUE_SCLEAN_FAILED Suite cleanup failed.
CUE_DUP_SUITE Duplicate suite name not allowed.
CUE_NOTEST A required CU_pTest pointer was NULL.
CUE_NO_TESTNAME Required CU_Test name not provided.
CUE_DUP_TEST Duplicate test case name not allowed.
CUE_TEST_NOT_IN_SUITE Test is not registered in the specified suite.
CUE_FOPEN_FAILED An error occurred opening a file.
CUE_FCLOSE_FAILED An error occurred closing a file.
CUE_BAD_FILENAME A bad filename was requested (NULL, empty, nonexistent, etc.).
CUE_WRITE_ERROR An error occurred during a write to a file.

出错时的行为定义如下表所示。

CUEA_IGNORE Runs should be continued when an error condition occurs (default)
CUEA_FAIL Runs should be stopped when an error condition occurs
CUEA_ABORT The application should exit() when an error conditions occurs