1. 程式人生 > >CUnit使用入門-精簡的C語言單元測試工具

CUnit使用入門-精簡的C語言單元測試工具

目的:通過一個示例演示,掌握用Cunit做單元測試
內容:
1、介紹Cunit架構
2、介紹Cunit的測試模式
3、例項演示用Cunit 寫單元測試的操作流程

1.Cunit架構
Test Registry

|

|                  |
Suite '1' . . . . Suite 'N'
|                  |
           | |                 | |

Test ‘11’ … Test ‘1M’ Test ‘N1’ … Test ‘NM’
提到這個框架,為後面如何使用CUnit提供了基礎。
先介紹這個框架,從底層往上介紹就兩句話:
(1)每個測試用例被包裝在一個測試包(suite)中,
(2)每個測試包(suite)是在有效的測試註冊單元(Test Registry)中註冊的。
對於CUnit來說,它是單執行緒執行,所以每次只能有一個有效的測試註冊單元(Test Registry),這個測試註冊單元下面可以包含多個測試包(suite),每個測試包可以擁有多個測試用例。劃分測試包(suite)的規則可以自由約定,比如按照模組來劃分,一個模組的測試用例集中到一個測試包中(suite)。至於測試用例,則用來測試模組內部的函式。測試用例函式通過提供的各類輸入呼叫被測試的函式,返回執行結果,然後通過CUnit提供的斷言來判斷被測試的函式是否正確。
2.Cunit的測試模式
1 Automated Output to xml file Non-interactive
2 Basic Flexible programming interface Non-interactive
3 Console Console interface (ansi C) Interactive
4 Curses Graphical interface (Unix) Interactive
第一種模式是將結果輸出到XML文件中,便於生成報告。
第二種模式是每一次執行結束之後在standard output中顯示測試結果,不能保留測試結果資料。
第三種模式是console方式的,可以人機互動;前兩種模式是非互動式的。第四種只在Unix中使用。

3.例項演示用Cunit寫單元測試的操作流程
1) 編寫單元測試函式(有必要的話要寫suite的init/cleanup函式)。Write functions for tests (and suite init/cleanup if necessary).
2) 呼叫函式CU_initialize_registry()初始化測試註冊單元(Test Registry)。 Initialize the test registry - CU_initialize_registry()
3) 呼叫函式CU_add_suite() 將測試包(suite)新增到測試註冊單元(Test Registry)中。Add suites to the test registry - CU_add_suite()
4) 呼叫函式CU_add_test()將測試用例新增到測試包(suite)中。Add tests to the suites - CU_add_test()
5) 使用合適的介面來執行測試用例。Run tests using an appropriate interface, e.g. CU_console_run_tests
6) 呼叫函式CU_cleanup_registry清除測試註冊單元(Test Registry)。Cleanup the test registry - CU_cleanup_registry()

4.Linux系統下用Cunit來進行單元測試
1.編譯CUnit,編譯後,標頭檔案目錄在/root/local/include/CUnit中,靜態庫檔案在/root/local/lib/下。
(1)用root使用者登入,下載CUnit-2.1-0-src.tar.gz。
(2)tar -zxvf CUnit-2.1-0-src.tar.gz
(3)cd CUnit-2.1-0
(4)./configure –prefix=$HOME/local
(5)make
(6)make install
2.編寫一個Makefile檔案,放入到在原始檔目錄中
2.Linux可執行檔案執行時找不到共享庫的解決辦法
錯誤資訊:error while loading libraries: libcunit.so.1 :cannot open shared object file:No such file or directory

1>ldd 命令檢視編譯生成的可執行檔案發現:
linux-gate.so.1 => (0x00498000)
libcunit.so.1 => not found
libc.so.6 => /lib/libc.so.6 (0x00b6f000)
/lib/ld-linux.so.2 (0x00b4b000)
2>修改/etc/ld.so.conf,新增libcunit.so.1的存放目錄/root/local/lib
3>Ldconfig
4>再次執行ldd命令
linux-gate.so.1 => (0x00a98000)
libcunit.so.1 => /root/local/lib/libcunit.so.1 (0x0068c000)
libc.so.6 => /lib/libc.so.6 (0x00b6f000)
/lib/ld-linux.so.2 (0x00b4b000)
5>原理:
編譯時確實是通過了,因為你編譯時指定了編譯時需要的有關該共享庫的資訊。
但是沒有指定執行時(run time)所需要的資訊。ldconfig creates the necessary links and cache to the most recent shared libraries found in the directories specified on the command line, in the file /etc/ld.so.conf, and in the trusted directories (/lib and /usr/lib). The cache(/etc/ld.so.cache
) is used by the run-time linker, ld.so or ld-linux.so.
需要的動態連結庫的目錄路徑,必須在/etc/ld.so.cache 檔案中,而該檔案是通過ld.so.conf指定的目錄,以及信任目錄(/lib and /usr/lib),使用ldconfig命令生成的。ldd命令也檢視可執行檔案所依賴的動態庫資訊時,也使用了/etc/ld.so.cache檔案,因此在沒有將所需要的動態庫目錄新增到ld.so.conf中且執行ldconfig時,會提示該庫”not found”

4:原始碼

⑴被測函式test.c
/**
*file:test.c
**/
int maxi(int i,int j)
{
    return i>j?i:j;
}
⑵測試函式(定義測試用例和測試包)testcase.c
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <CUnit/CUnit.h>
#include <CUnit/Automated.h>
#include <CUnit/TestDB.h>
/**//*---- functions to be tested ------*/
extern int maxi(int i, int j);
/**//*---- test cases ------------------*/
void testIQJ()
{
    CU_ASSERT_EQUAL(maxi(11),1);
    CU_ASSERT_EQUAL(maxi(0,-0),0);
}
void testIGJ()
{
    CU_ASSERT_EQUAL(maxi(21),2);
    CU_ASSERT_EQUAL(maxi(0,-1),0);
    CU_ASSERT_EQUAL(maxi(-1,-2),-1);
}
void testILJ()
{
    CU_ASSERT_EQUAL(maxi(12),2);
    CU_ASSERT_EQUAL(maxi(-10),0);
    CU_ASSERT_EQUAL(maxi(-2,-1),-1);
}
CU_TestInfo testcases[] = {
    {"Testing i equals j:", testIQJ},
    {"Testing i greater than j:", testIGJ},
    {"Testing i less than j:", testILJ},
    CU_TEST_INFO_NULL
};
/**//*---- test suites ------------------*/
int suite_success_init(void)
{ return 0; }
int suite_success_clean(void)
{ return 0; }
CU_SuiteInfo suites[] = {
    {
        "Testing the function maxi:",      suite_success_init,              suite_success_clean,
         testcases
     },
    CU_SUITE_INFO_NULL
};
/**//*---- setting enviroment -----------*/
void AddTests(void)
{
    assert(NULL != CU_get_registry());
    assert(!CU_is_test_running());
/**//* shortcut regitry */
    if(CUE_SUCCESS != CU_register_suites(suites)){
            fprintf(stderr, "Register suites failed - %s ", CU_get_error_msg());
    exit(EⅪT_FAILURE);
    }
}
⑶執行測試函式 Main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "Basic.h"
int main(int argc, char* argv[])
{
    CU_BasicRunMode mode = CU_BRM_VERBOSE;
    CU_ErrorAction error_action = CUEA_IGNORE;
    int i;
    setvbuf(stdout, NULL, _IONBF, 0);
    for (i=1 ; i<argc ; i++) {
        if (!strcmp("-i", argv)) {
            error_action = CUEA_IGNORE;
        }
        else if (!strcmp("-f", argv)) {
            error_action = CUEA_FAIL;
        }
        else if (!strcmp("-A", argv)) {
            error_action = CUEA_ABORT;
        }
        else if (!strcmp("-s", argv)) {
            mode = CU_BRM_SILENT;
        }
        else if (!strcmp("-n", argv)) {
            mode = CU_BRM_NORMAL;
        }
        else if (!strcmp("-v", argv)) {
            mode = CU_BRM_VERBOSE;
        }
        else if (!strcmp("-e", argv)) {
            return 0;
        }
        else {
            printf("\nUsage:BasicTest [options]\n\n"
            "Options:-i ignore framework errors [default].\n"
            " -f fail on framework error.\n"
            " -A abort on framework error.\n\n"
            " -s silent mode - no output to screen.\n"
            " -n normal mode - standard output to screen.\n"
            " -v verbose mode - max output to screen [default].\n\n"
            " -e print expected test results and exit.\n"
            " -h print this message and exit.\n\n");
            return 0;
    }
}
if (CU_initialize_registry()) {
    printf("\nInitialization of Test Registry failed.");
}
else {
        AddTests();
        CU_basic_set_mode(mode);
        CU_set_error_action(error_action);
        printf("\nTests completed with return value %d.\n", CU_basic_run_tests());
        CU_cleanup_registry();
    }
    return 0;
}
⑷Makefile
INC=-I/usr/local/include/CUnit
LIB=-L/usr/local/lib/
all:func.c test_func.c run_test.c
#gcc -o test $(INC) $(LIB) -lcunit $^
gcc -o test $(INC) $(LIB) -lcunit  $^
clean:
-rm -rf *.o test