【單元測試】CUnit單元測試框架(不支持mock功能)

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可以找到很多範例。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章