使用CUNIT测试
一:概述
CUnit是一个c语言的单元测试框架,它是以静态链接库的形式,连接到用户代码中的,主要的功能就是提供了语义丰富的断言和多种测试结果输出接口,可以方便地生成测试报告。
但是需要注意的地方是,由于Cunit和我们的代码是在同一个项目中,所以,需要注意将测试代码和程序代码进行区分管理,避免直接在程序代码中添加测试代码;为了达到这个目的,我们经常需要提供单独的头文件,在这个头文件中,可以将原有接口函数罗列进来,还可以将需要测试的内部使用的函数列入,这样,在测试用的.c文件中,就可以直接引用该头文件进行编译,连接,测试。
一个Registry可以认为是一个需要测试的独立的功能集合单元,我们可以使该单元处于active状态,以便进行运行测试,也可以使之处于inactive状态,这样,在一个运行中,我们可以指定要运行的Registry。
一个Suite可以若干运行条件类似的测试的集合,他需要注册到某一个Registry才可以被运行到。
划分Suite的标准是多样的,例如,某些test运行之前需要进行特定的初始化动作,那么我们可以把凡是需要该类初始化动作的test放入到一个Suite中,因为以Suite为单位,可以有自己的初期化函数和清理函数。
一个test就可以认为是一个单元测试的函数了,由于Cunit是一个黑盒测试工具,也就是说,他的主要目的是根据输入参数和返回结果来从外部观察函数执行的是否正确,所以,通常的做法就是我们提供多种输入,然后使用Cunit提供的断言,来判断返回值,out形参数,和函数可能影响的全局变量的变化是否符合我们的设计。
Cunit提供的多种编程接口,通常是针对不同类型的程序需求。
Automated | Output to xml file | Non-interactive |
Basic | Flexible programming interface | Non-interactive |
Console | Console interface (ansi C) | Interactive |
Curses | Graphical interface (Unix) | Interactive |
如上边所示,后两种主要是interactive的接口,就是我们可以交互式地指定参数,然后观察结果。在我们具体的环境下,我们通常使用上面两种接口,第一种是将结果输出到XML文档中,便于我们生成报告。第二种仅仅是每一次运行结束之后,在standard output中显示,不能保留测试结果数据。
我们可以将前两种输出结合起来,自己测试的时候,使用Basic模式,生成报告的时候,使用Automated模式。
二:详细介绍
1:测试函数的书写
下面介绍一下典型的测试程序的书写流程:
1) 首先针对被测试的函数书写测试函数。
2) fopen函数失败初始化一个Registry。
3) 将特定的Suite加入到这个Registry中。可以为Suite指定初始化函数和清理函数。
4) 将测试函数加入到一个Suite中,这样,如果该测试函数的运行需要一些初始化条件,那么可以可以将代码加入到Suite的初始化函数中,当然不要忘记最后还要做对应的清理操作。
5) 使用相应的接口将测试结果输出。
6) 最后,清理Registry。
下面的程序是一个典型的例子:程序段1如下
这个例子中,有两个Suite,同时自己定义了一个函数 来将test加入到Suite中,这样的目的是避免main函数太长。另外这个函数中同时使用了Automated接口和Basic接口。我们不必同时使用,可以根据需要的形式进行选择。需要注意的是,使用Automated接口是,需要调用CU_list_tests_to_file()函数。
2:CUnit提供的断言
CUnit提供了大量的预定义的断言,针对几乎所有的C的标准类型,我们可以针对具体的需要检查的参数的类型进行选择。
提供的断言有:
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 andexpected 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 andexpected 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. |
可以看到,每一种类型的断言都有FATAL和非FATAL两个函数,他们的区别是,非FATAL断言失败的时候,程序继续运行,而FATAL类型的断言失败之后,程序马上中止。通常我们使用非FATAL就可以了。
注意在我们的程序中,需要对返回值,out参数进行判断,对于是否是预想结果的判断的时候,一定要使用断言,而不要使用printf等等函数,一方面printf缺乏灵活性,另一方面,只有使用断言,结果报告中才会有对应的输出项。
CU_PASS,CU_FAIL这两个断言比较特殊,它们仅仅表示测试程序运行到了这个地方。比如,在某些测试函数中,被测试的函数没有任何返回值等,我们为了证明这个函数已经被运行到了,我们使用以上两个函数。它们仅仅打印出一条消息,代表执行过。还有就是,它们也被输出。
下面程序段2给出了一个被测函数和一个测试函数,注意断言的使用:
测试用例是否完备,需要测试人员自己保证。
3:注册所要进行的测试
测试程序书写完成之后,我们需要将它们添加到某一个Suite中,在程序段1中我们看到了如何将一个Suite添加到Registry中,下面我们看看Suite的初始化和清理函数,以及如何将tests添加到Suite中。以下是Suite的初始化和清理程序段3:
这两个函数是在将Suite添加到Registry的时候指定的。参见程序段1。CU_add_suite函数的参数。
下面程序段4显示如何将tests加入Suite,以及测试函数本身
其中,testWRITE_GROUP是一个函数名。函数如下:
其中,write_group就是被测函数。
4:运行测试和测试报告的生成
完成了以上步骤之后,剩下的就是生成测试报告了:以下是Basic模式下的典型的输出:
表中显示了一共有多少个Suite,多少个tests,还有一共执行了多少个断言.(当然,以上这么多断言是因为有循环。)
如果有失败的断言,那么会是如下的样子,在程序中,我们将write_group进行调整,让断言失败。
以上可以看出,报告会指出断言失败的tests数目,还可以指出,断言失败的位置和函数。
关于Automated模式的,如下所示:表一是测试结果的报告,显示该XML文档,需要相关的XSL文档和DTD文档。test_report_multiuser-Results
Running Suite Suite(need not init multiuser data) | |||
Running test test of get_key() ... | Passed | ||
Running test test of original crypt and decrypt ... | Passed | ||
Running test test of read_group() ... | Passed | ||
Running test test of crypt and decrypt ... | Passed | ||
Running test test of mu_load_multiuser_files() ... | Passed | ||
Running test test of test parameter check functions ... | Passed | ||
Running test test of free_list() ... | Passed | ||
Running Suite Mysuite(need load multiuser data | |||
Running test test of get_admin_info() ... | Passed | ||
Running test test of mu_get_all_user_info() ... | Passed | ||
Running test test of find_user() ... | Passed | ||
Running test test of write_group() ... | Passed | ||
Running test test of mu_login() ... | Passed | ||
Running test test of mu_logout() ... | Passed | ||
Running test test of mu_islogin() ... | Passed | ||
Running test test of mu_user_authentication() ... | Passed | ||
Running test test of mu_modify_password() ... | Passed | ||
Running test test of mu_modify_settings() ... | Passed | ||
Running test test of mu_get_user_settings() ... | Passed | ||
Running test test of mu_register() ... | Passed | ||
Running test test of mu_pop_is_busy() ... | Passed | ||
Running test test of add_to_list and remove_from_list ... | Passed | ||
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论