CUnit是一个通过C语言编写单元测试框架,用于编写、管理和执行单元测试用例的测试系统。
有关CUnit框架的更多介绍,可以参阅CUnit用户手册:
本文重点讲解如何在Linux环境下使用CUnit框架实现单元测试。
下载地址:CUnit-2.1-3.tar.bz2
这是截止书稿本文时最新的版本。
# uname -vom
#35-Ubuntu SMP Fri Aug 10 17:58:07 UTC 2018 x86_64 GNU/Linux
# lsb_release -d
Description: Ubuntu 18.04.2 LTS
# tar -jxvf CUnit-2.1-3.tar.bz2
# cd CUnit-2.1-3
# mv configure.in configure.ac
# aclocal
# autoconf
# autoheader
# libtoolize
# automake --add-missing
# automake
# mkdir
# ./configure --prefix
# make
# make install
以下是我编译后的结果:
# tree -L 3 /opt/CUnit
/opt/CUnit
├── doc
│ └── CUnit
│ ├── CUnit_doc.css
│ ├── error_handling.html
│ ├── fdl.html
│ ├── headers
│ ├── index.html
│ ├── introduction.html
│ ├── managing_tests.html
│ ├── running_tests.html
│ ├── test_registry.html
│ └── writing_tests.html
├── include
│ └── CUnit
│ ├── Automated.h
│ ├── Basic.h
│ ├── Console.h
│ ├── CUError.h
│ ├── CUnit.h
│ ├── CUnit_intl.h
│ ├── MyMem.h
│ ├── TestDB.h
│ ├── TestRun.h
│ └── Util.h
├── lib
│ ├── libcunit.a
│ ├── libcunit.la
│ ├── libcunit.so -> libcunit.so.1.0.1
│ ├── libcunit.so.1 -> libcunit.so.1.0.1
│ ├── libcunit.so.1.0.1
│ └── pkgconfig
│ └── cunit.pc
└── share
├── CUnit
│ ├── CUnit-List.dtd
│ ├── CUnit-List.xsl
│ ├── CUnit-Run.dtd
│ ├── CUnit-Run.xsl
│ ├── Memory-Dump.dtd
│ └── Memory-Dump.xsl
└── man
└── man3
11 directories, 31 files
写一个简单的demo进行演示,使用CUnit框架对sum求和函数(内部故意安装一个BUG)进行单元测试,如下代码所示(文件名为example1.c):
#include
#include
#include "CUnit/Basic.h"
/* 被测试的函数,在当中故意安装了一个BUG */
static int sum(int a, int b)
{
if (a > 4) {
return 0;
}
return (a + b);
}
static int suite_init(void)
{
return 0;
}
static int suite_clean(void)
{
return 0;
}
static void test_sum(void)
{
CU_ASSERT_EQUAL(sum(1, 2), 3);
CU_ASSERT_EQUAL(sum(5, 2), 7);
}
int main()
{
CU_pSuite pSuite = NULL;
/* initialize the CUnit test registry */
if (CUE_SUCCESS != CU_initialize_registry()) {
return CU_get_error();
}
/* add a suite to the registry */
pSuite = CU_add_suite("suite_sum", suite_init, suite_clean);
if (NULL == pSuite) {
CU_cleanup_registry();
return CU_get_error();
}
/* add the tests to the suite */
if ((NULL == CU_add_test(pSuite, "test_sum", test_sum))) {
CU_cleanup_registry();
return CU_get_error();
}
/* Run all tests using the CUnit Basic interface */
CU_basic_set_mode(CU_BRM_VERBOSE);
CU_basic_run_tests();
/* Clean up registry and return */
CU_cleanup_registry();
return CU_get_error();
}
编译:
# gcc -o test example1.c -I/opt/CUnit/include /opt/CUnit/lib/libcunit.a
运行结果:
# ./test
CUnit - A unit testing framework for C - Version 2.1-3
http://cunit.sourceforge.net/
Suite: suite_sum
Test: test_sum ...FAILED
1. example1.c:29 - CU_ASSERT_EQUAL(sum(5, 2),7)
Run Summary: Type Total Ran Passed Failed Inactive
suites 1 1 n/a 0 0
tests 1 1 0 1 0
asserts 2 2 1 1 n/a
Elapsed time = 0.000 seconds
测试结果一目了然,执行失败的测试用例也罗列出来,很直观。
除了示例1的代码,同样的功能,还可以使用 CU_register_suites 函数代替 CU_add_suite 和 CU_add_test 接口,如下示例2所示,大家可以自由选择使用哪种方式,殊途同归:
#include
#include
#include "CUnit/Basic.h"
/* 被测试的函数,在当中故意安装了一个BUG */
static int sum(int a, int b)
{
if (a > 4) {
return 0;
}
return (a + b);
}
static int suite_init(void)
{
return 0;
}
static int suite_clean(void)
{
return 0;
}
static void test_sum(void)
{
CU_ASSERT_EQUAL(sum(1, 2), 3);
CU_ASSERT_EQUAL(sum(5, 2), 7);
}
static CU_TestInfo tests_sum[] = {
{ "test_sum", test_sum },
CU_TEST_INFO_NULL,
};
static CU_SuiteInfo suites[] = {
{ "suite_sum", suite_init, suite_clean, NULL, NULL, tests_sum },
CU_SUITE_INFO_NULL,
};
int main()
{
CU_pSuite pSuite = NULL;
/* initialize the CUnit test registry */
if (CUE_SUCCESS != CU_initialize_registry()) {
return CU_get_error();
}
/* Register suites. */
if (CUE_SUCCESS != CU_register_suites(suites)) {
CU_cleanup_registry();
return CU_get_error();
}
/* Run all tests using the CUnit Basic interface */
CU_basic_set_mode(CU_BRM_VERBOSE);
CU_basic_run_tests();
/* Clean up registry and return */
CU_cleanup_registry();
return CU_get_error();
}
CUnit执行结果输出支持四种模式,分别为:
模式 | 支持的平台 | 执行结果输出方式 | 备注 |
---|---|---|---|
Basic | All | 输出到标准输出 | 最常用的,结果输出到标准输出(stdout) |
Automated | All | 输出到xml文件 | 生成完XML文件之后,然后再将CUnit-List.dtd、CUnit-List.xsl、CUnit-Run.dtd、CUnit-Run.xsl(这几个文件在CUnit的源码包可以找到)和XML文件放到同一级目录,再用IE浏览器打开,就可以看到漂亮的界面了。 |
Console | All | 交互式控制台方式 | 比较灵活,可以选择只执行其中某一个测试用例。 |
Curses | Linux/Unix | 交互式curses窗口方式 | 跟Console类似,只不过是以Curses窗口的方式展示。 |
前面示例展示的就是Basic模式,在CUnit官网的 Screenshots 中可以看到各个模式的效果图。
在代码中要使用不同模式,只要调用相应的接口函数即可(也可以同时使用多种模式):
模式 | 使用的接口函数 |
---|---|
Basic | #include "CUnit/Basic.h" CU_basic_set_mode(CU_BRM_VERBOSE); CU_basic_run_tests(); |
Automated | #include "CUnit/Automated.h" CU_list_tests_to_file(); CU_automated_run_tests(); |
Console | #include "CUnit/Console.h" CU_console_run_tests(); |
Curses | #include "CUnit/CUCurses.h" CU_curses_run_tests(); |
断言是单元测试中重要的组成部分,CUnit的断言定义在 #include
断言 | 描述 |
---|---|
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. |
注意:带有 FATAL 的断言遇到错误时,将中止测试,即后面的测试用例将不会执行;不带 FATAL 的断言遇到错误时,会继续测试后面的测试用例。
这些断言并不会让进程异常,即使带有 FATAL 的断言遇到错误时,也只是不再测试后面的测试用例,但通过CU_add_suite 注册的 CU_CleanupFunc 函数依然会被执行。