1. 程式人生 > >嵌入式單元測試--框架解析

嵌入式單元測試--框架解析

test ble 記錄 開發者 並且 .... 就是 規劃 復雜度

1.單元測試的必要性

單元測試是軟件開發的重要一環,尤其對嵌入式開發。因為嵌入式開發受限於開發環境、調試工具等因素,不能和純PC軟件開發一樣使用很多先進的工具。這就需要開發者在開發過程中,進行更細的模塊劃分,更明確的接口,更詳盡的測試。根據軟件工程理論,1個bug越是在後期越是花費巨大的成本去修復,並且隨著系統復雜度的增長,在一個大的系統中去查找某一個細節具體的問題,相比於在小的模塊中去查找問題會花費多倍的時間成本。

2.單元測試框架解剖

一般地單元測試需要實現以下幾個基本功能:
1. assert
各種assert,比如AssertTrue、比如AssertFail、AssertStrEquals、AssertIntEquals......
條條大路通羅馬,這些Assert有各種功能,其實就是包裝了斷言的函數。比如AssertStrEquals(str, "open"),進行str和字符串“open”的比較,如果不相同則會報錯。
根據框架的結構,在assert失敗時候,有的進行長跳轉longjmp,有的對類似failCount的全局的變量進行加1並記錄錯誤位置。
2. 錯誤位置記錄


得益於C語言的LINE、FILE宏,這是2個ANSI C標誌支持的內置宏定義,可以得到當前的的行數和文件名。 在斷言失敗的地方,記錄文件名和行號,以供用戶查詢錯誤的位置。

char buf[HUGE_STRING_LEN];
sprintf(buf, "%s:%d: ", _FILE_, _LINE_);

3. 測試case管理
這是測試框架區別於自己寫的assert測試函數最根本的地方。 測試框架為了提高函數利用率,減少重復,方便測試例程匯總等,都會進行各種封裝。比如以下幾條。
1)setup和teardown
大部分的測試框架都提供這兩個函數,主要是因為有些測試case,有大量重復的代碼,比如準備輸入數據,測試完畢後清理現場等通用的功能。
2)測試例子匯總
有的叫做TestSuit,有的叫做TestFixtures。把一類相似功能的測試case進行匯總,方便更高層次的調用,也方便用戶管理測試例程。
3)測試的調用
多個測試例程匯總後,構成一個數組(表格),啟動運行,一般由xxxRun函數負責。
在嵌入式c中,一般都有一個函數指針來操作,這也是為什麽所有的測試case的函數名稱都使用相同的聲明,test_case需要和調用該測試的指針同類型。

4. 測試的執行
測試的執行本質就是函數的長跳轉。可以看做在父函數中調用子函數,這個子函數如果是測試例程的話,子函數就會包含assert相關的語句,而assert語句在出錯後,會記錄錯位位置和錯誤消息,然後進行長跳轉(longjmp),longjmp和setjmp(buf)成對出現,返回到調用的位置,然後進行下一個測試case。

    for (i = 0 ; i < testSuite->count ; ++i)
    {
        Test* testCase = testSuite->list[i];
        TestRun(testCase);
        
if (testCase->failed) { testSuite->failCount += 1; } }

3. 測試框架的本質

1)為了更好的組織測試,提供的測試組的批量處理功能,一般由for循環遍歷一個table數組實現;
2)為了減少重復進行測公用函數提取,比如準備測試環境和清理現場;
3)測試需要的各種斷言;
4)斷言失敗後的跳轉、記錄錯誤位置-FILE-, -LINE-宏的使用;
5)測試case運行的監控和結果的匯總。


綜上,如果你實現了上面的幾個功能,那麽也就自己完成了一個測試框架。
其實測試框架是一個很簡單的事情,如今測試框架有很多,像VS這樣的IDE已經集成了單體測試,所以對於一個開發者怎麽規劃測試才是測試工作的第一要務。
如何恰當的寫測試用例,既不延誤開發又不會造成工程臃腫,還能盡可能的覆蓋測試範圍,這才是測試中最花費功夫的地方。

嵌入式單元測試--框架解析