C語言單元測試

C語言單元測試

 對於敏捷開發來說,單元測試必不可少,對於Java開發來說,JUnit非常好,對於C++開發,也有CPPUnit可供使用,而對於傳統的C語言開發,就沒有很好的工具可供使用,可以找到的有這麼幾個工具:

  1.  CuTest -- CuTest(Cute Test)是一個非常簡單的C語言單元測試工具。在使用它的時候,只需要包含兩個文件“CuTest.c CuTest.h”,然後就可以寫測試用例,進行測試了。它對用例幾乎沒有管理功能,報表輸出也非常簡單,可以用來試驗單元測試的基本想法。
  2. CUnit -- CUnit是一個輕型的C語言單元測試框架。它提供了設計、管理、運行測試用例的功能。它的報表功能比較強大,但是比較麻煩,更適合於較大一些的項目。
  3. Check -- 不錯的工具。
    在這裏(http://www.laatuk.com/tools/testing_tools.html)給出了各種軟件測試工具,沒事可以研究一下。

CUnit

這裏主要講CUnit在Linux平臺下的應用。這裏有一篇 CUnit測試工具使用 ,另一篇 C單元測試包設計與實現 講的不錯,可以看一下。CUnit的主頁是 http://cunit.sourceforge.net/index.html
CUnit以靜態庫的形式提供給用戶使用,用戶編寫程序的時候直接鏈接此靜態庫就可以了。它提供了一個簡單的單元測試框架,並且爲常用的數據類型提供了豐富的斷言語句支持。

CUnit基本架構

                       Test Registry
                            |
             ------------------------------
             |                            |
          Suite '1'      . . . .       Suite 'N'
             |                            |
       ---------------             ---------------
       |             |             |             |
    Test '11' ... Test '1M'     Test 'N1' ... Test 'NM'

一次測試(Test Registry)可以運行多個測試包(Test Suite),而每個測試包可以包括多個測試用例(Test Case),每個測試用例又包含一個或者多個斷言類的語句。具體到程序的結構上,一次測試下轄多個Test Suite,它對應於程序中各個獨立模塊;一個Suite管理多個Test Case,它對應於模塊內部函數實現。每個Suite可以含有setup和teardown函數,分別在執行suite的前後調用。

CUnit測試模式

CUnit使用四種不同的接口,供用戶來運行測試和彙報測試結果:

  1. 自動輸出到XML文件,     非交互式
  2. 基本擴展編程方式,        非交互式
  3. 控制檯方式,              交互式
  4. Curses圖形接口,          交互式

注意1和2是非交互式的,4只能在Unix下使用,常用console,而且console是可以人機交互的。

CUnit測試流程

使用CUnit進行測試的基本流程如下所示:

  1. 書寫代測試的函數(如果必要,需要寫suite的init/cleanup函數)
  2. 初始化Test Registry - CU_initialize_registry()
  3. 把測試包(Test Suites)加入到Test Registry - CU_add_suite()
  4. 加入測試用例(Test Case)到測試包當中 - CU_add_test()
  5. 使用適當的接口來運行測試測試程序,例如 CU_console_run_tests()
  6. 清除Test Registry - CU_cleanup_registry()

CUnit使用範例

CUnit的在線文檔是 http://cunit.sourceforge.net/doc/index.html ,上面有着詳細的論述。這裏以使用自動產生XML文件的接口爲例,講述CUnit-2.1-0在Linux平臺下的使用。
我要測試的是整數求最大值的函數maxi,我使用如下文件組織結構:

  1. func.c :定義maxi()函數
  2. test_func.c :定義測試用例和測試包
  3. run_test.c :調用CUnit的Automated接口運行測試
  4. Makefile :生成測試程序。

這樣組織的好處是,我們可以把各個功能分離,當要改變待測試函數的定義的時候,我們只需要修改func.c,而要增減、修改測試用例,只修改test_func.c就可以了,要使用CUnit提供的別的API,那就修改run_test.c。

它們的內容分別如下所示:

1) func.c

/**
 * file: func.c
 *
*/


int maxi(int i, int j)
{
        
//return i>j?i:j;
        return i;
}

2) test_func.c

/**
 * file: test_func.c
 *
*/

#include 
<stdio.h>
#include 
<stdlib.h>
#include 
<assert.h>
#include 
"CUnit/CUnit.h"
#include 
"CUnit/Automated.h" 

/*---- functions to be tested ------*/
extern int maxi(int i, int j); 

/*---- test cases ------------------*/
void testIQJ()
{
        CU_ASSERT_EQUAL(maxi(
1,1),1);
        CU_ASSERT_EQUAL(maxi(
0,-0),0);
}

 
void testIGJ()
{
        CU_ASSERT_EQUAL(maxi(
2,1),2);
        CU_ASSERT_EQUAL(maxi(
0,-1),0);
        CU_ASSERT_EQUAL(maxi(
-1,-2),-1);
}

 
void testILJ()
{
        CU_ASSERT_EQUAL(maxi(
1,2),2);
        CU_ASSERT_EQUAL(maxi(
-1,0),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(voidreturn 0; }
int suite_success_clean(voidreturn 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(EXIT_FAILURE);
        }

}

 3) run_test.c

/**
 * file: run_test.c
 *
*/


#include 
<stdio.h>
#include 
<stdlib.h>
#include 
<assert.h>

int main( int argc, char *argv[] )
{
       
if(CU_initialize_registry()){
                fprintf(stderr, 
" Initialization of Test Registry failed. ");
                exit(EXIT_FAILURE);
        }
else{
                AddTests();
                CU_set_output_filename(
"TestMax");
                CU_list_tests_to_file();
                CU_automated_run_tests();
                CU_cleanup_registry();
        }

        
return 0;
}

4) Makefile 

INC=-I/home/lirui/local/include
LIB
=-L/home/lirui/local/lib

all: func
.c test_func.c run_test.c
        gcc -o test 
$(INC) $(LIB)-lcunit -lcurses -static $^

由於CUnit是以庫的形式提供的,所以我們在編譯和鏈接的時候需要指明頭文件和庫所在的位置,又由於使用了Curses庫,所以也要指定這個。

測試報告

 運行上面產生的test程序,會在當前目錄下產生兩個xml文件:

  1. TestMax-Listing.xml :對測試用例的報告
  2. TestMax-Results.xml :對測試結果的報告

要查看這兩個文件,需要使用如下xsl和dtd文件:CUnit-List.dtd和CUnit-List.xsl用於解析列表文件, CUnit-Run.dtd和CUnit-Run.xsl用於解析結果文件。這四個文件在CUnit包裏面有提供,安裝之後在$(PREFIX) /share/CUnit目錄下,在我的配置當中,它在/home/lirui/local/share/CUnit目錄下。在查看結果之前,需要把這六個文件:TestMax-Listing.xml, TestMax-Results.xml, CUnit-List.dtd, CUnit-List.xsl, CUnit-Run.dtd, CUnit-Run.xsl拷貝到一個目錄下,然後用瀏覽器打開兩個結果的xml文件就可以了。

1) TestMax-Listing.xml在IE當中顯示如下:

list cases

2) TestMax-Results.xml在IE當中顯示如下:

results

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章