單元測試是軟件開發的重要一環,尤爲對嵌入式開發。由於嵌入式開發受限於開發環境、調試工具等因素,不能和純PC軟件開發同樣使用不少先進的工具。這就須要開發者在開發過程當中,進行更細的模塊劃分,更明確的接口,更詳盡的測試。編程
傳統的開發先出設計方案,而後跟着寫出代碼,最後在作測試---常被叫作bug調試,因此在代碼「寫完」以後,還有一半左右的開發量。人都會犯錯,在設計和編碼中也會犯錯,若是後期調試編程去解決設計和編碼中引入的錯誤,那麼可能已通過了幾天幾周幾個月,反饋如此只晚,已經不能從錯誤中吸收經驗了,下次還會犯一樣的錯誤。此外根據軟件工程理論,1個bug越是在後期越是花費巨大的成本去修復,而且隨着系統複雜度的增加,在一個大的系統中去查找某一個細節具體的問題,相比於在小的模塊中去查找問題會花費多倍的時間成本。數組
通常地單元測試須要實現如下幾個基本功能:
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; } }
1)爲了更好的組織測試,提供的測試組的批量處理功能,通常由for循環遍歷一個table數組實現;
2)爲了減小重複進行測公用函數提取,好比準備測試環境和清理現場;
3)測試須要的各類斷言;
4)斷言失敗後的跳轉、記錄錯誤位置-FILE-, -LINE-宏的使用;
5)測試case運行的監控和結果的彙總。單元測試
綜上,若是你實現了上面的幾個功能,那麼也就本身完成了一個測試框架。
其實測試框架是一個很簡單的事情,現在測試框架有不少,像VS這樣的IDE已經集成了單體測試,因此對於一個開發者怎麼規劃測試纔是測試工做的第一要務。
如何恰當的寫測試用例,既不延誤開發又不會形成工程臃腫,還能儘量的覆蓋測試範圍,這纔是測試中最花費功夫的地方。測試