1 CUnit簡介
CUnit是一個通過C語言編寫單元測試框架,用於編寫、管理和執行單元測試用例的測試系統。
- 官網:http://cunit.sourceforge.net/
- 幫助文檔:http://cunit.sourceforge.net/documentation.html
有關CUnit框架的更多介紹,可以參閱CUnit用戶手冊:
- 官方英文版:http://cunit.sourceforge.net/doc/index.html
- 中文翻譯版:https://blog.csdn.net/benkaoya/article/details/95887879
本文重點講解如何在Linux環境下使用CUnit框架實現單元測試。
2 下載CUnit
下載地址:CUnit-2.1-3.tar.bz2
這是截止書稿本文時最新的版本。
3 編譯CUnit
3.1 我的環境
# 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
3.2 編譯安裝
# 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 <install directory>
# ./configure --prefix <install directory>
# 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
4 開始使用
4.1 示例1
寫一個簡單的demo進行演示,使用CUnit框架對sum求和函數(內部故意安裝一個BUG)進行單元測試,如下代碼所示(文件名爲example1.c):
#include <stdio.h>
#include <string.h>
#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
測試結果一目瞭然,執行失敗的測試用例也羅列出來,很直觀。
4.2 示例2
除了示例1的代碼,同樣的功能,還可以使用 CU_register_suites 函數代替 CU_add_suite 和 CU_add_test 接口,如下示例2所示,大家可以自由選擇使用哪種方式,殊途同歸:
#include <stdio.h>
#include <string.h>
#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();
}
4.3 四種模式輸出
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(); |
4.4 CUnit斷言
斷言是單元測試中重要的組成部分,CUnit的斷言定義在 #include <CUnit/CUnit.h> 頭文件中,在 CUnit-2.1-3 版本中,所有的斷言如下表所示:
斷言 | 描述 |
---|---|
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 函數依然會被執行。
4.5 更多參考
- CUnit官網Users Guide:http://cunit.sourceforge.net/doc/index.html
- GitHub範例:以關鍵詞 CU_ASSERT 搜索GitHub可以找到很多範例。