前言
嵌入式行業摸爬滾打這幾年,碰見有規範單元測試的項目寥寥無幾。歸根到底,無非是公司但願快速迭代出產品,有問題等客戶反饋再說。固然,也有人認爲是嵌入式行業都是小而美的產品居多,沒有到必定量級以前,玩不起單元測試這種配置。正如作個蛋炒飯,並不須要安排主廚、二廚通常。linux
不過出於對代碼穩定性的追求,我認爲仍是應該着手瞭解一下單元測試的。畢竟,這是有效提升代碼說服力的方式之一。編程
相信沒有真正體驗過單元測試好處的讀者一看到"單元測試"這幾個字,可能會出現如下兩種反應之一:c#
因爲沒有單元測試的經驗,所以對採用這一方法去保證軟件質量很好奇,也迫切地想要了解這一方法在項目中的實施性能優化
曾經使用單元測試但效果很差,由於在嵌入式行業,時常要跟硬件打交道,單元測試很難檢測硬件問題,因此每每一看到"單元測試"這幾個字的反應就是"沒用"網絡
若是讀者是第一種反應那很好,本文就是科普單元測試的基本要點。若是讀者是第二種反應,那多是對單元測試存在偏見,本系列文章也會介紹mock測試、錯誤注入等方式,使單元測試也能合理使用於嵌入式行業。框架
01函數
單元測試真的"無用"?性能
形成"單元測試無用論"的第一個緣由是,運用這一方法的時機不恰當。很多項目在一開始真正關心質量的人不多,更談不上採用一整套的方法論去保證質量了。產品在開發出來後發現處處存在問題,只會拆西牆補東牆根本就不能阻止問題一而再,再而三地出現。因而,開始想起單元測試。一聲令下,整個項目開始作單元測試。單元測試以模塊爲單位,須要先把項目拆分出來。若是你的項目代碼總體耦合程度較高的話,單元測試根本無從提及,拆分的工做會讓你痛苦不已。
單元測試
單元測試是一項耗時的工做,但管理者卻每每但願在短時間內看到效果。或者單元測試還沒作到位管理層就等不及了,催你立刻開始下一步的開發,結果只能是前功盡棄。正確的作法是:在項目的開始之初就引入單元測試。對於之前沒有部署單元測試的項目,先只對新增長的、相對獨立的模塊作單元測試、並逐漸覆蓋老代碼。學習
第二個致使"單元測試無用論"的緣由是,方法沒有運用到位。要保證單元測試的有效性必定要引入另外一個概念--代碼覆蓋。關於代碼覆蓋,我之後會另外再寫一篇文章介紹。只有將單元測試和代碼覆蓋結合在一塊兒,綜合使用才能保證單元測試的效果。
02
最原始的"單元測試"
這裏給讀者展現一下,不使用任何單元測試框架時,是怎麼作單元測試的。
下面簡單以linux內核鏈表爲例:
struct list_head { struct list_head *next, *prev; }; /*定義一個結構體,只含有表示前驅和後繼的指針,它就是咱們的主角了*/ #define LIST_HEAD_INIT(name) { &(name), &(name) } /*靜態初始化*/ #define LIST_HEAD(name) \ struct list_head name = LIST_HEAD_INIT(name) /*動態初始化*/ static inline void INIT_LIST_HEAD(struct list_head *list) { list->next = list; list->prev = list; } /*插入操做*/ /*刪除操做*/ /*合併操做*/ ...
完整代碼很長,這裏沒有必要所有貼出,能起演示做用就足夠了。
如今就以INIT_LIST_HEAD函數爲例,來考慮如何爲這個函數設計測試用例。INIT_LIST_HEAD函數的實現是如此的簡單,以致於很容易讓人以爲爲它設計單元測試是多餘的。可是,從單元測試的角度看,只要不存在可行性問題就不該考慮由於簡單而不對其進行驗證。並且,放棄對之進行驗證,之後會下降代碼覆蓋率。
作單元測試須要經過編寫程序的方式來完成,所編寫的用於測試的代碼又稱爲單元測試用例。
下面咱們來簡單實現一個INIT_LIST_HEAD函數的測試用例:
int main(int argc,char **argv) { struct list_head list; /*避免函數沒有使用參數而引起waining*/ UNUSED(argc); UNUSED(argv); list.prev = (struct list_head*)0xaaaa; list.next = (struct list_head*)0xbbbb; INIT_LIST_HEAD(list); /*檢查前指針*/ if(list.prev != list){ return -1; } /*檢查後指針*/ if(list.next != list){ return -1; } return 0; }
這應該是史上最簡單的測試用例,功能很是簡單,首先是故意將list結構體中的各個指針變量初始化爲一個隨機值。而後在調用完INIT_LIST_HEAD函數以後,檢查各成員是否被初始化爲了list,以判斷INIT_LIST_HEAD函數是否正常工做了。注意:這個測試程序還有一個約定,返回-1表明測試失敗,返回0表示測試成功。
這個測試用例是基於咱們對INIT_LIST_HEAD函數有足夠的瞭解以後編寫的,這種測試方法在軟件測試領域有個正兒八經的名字,叫白盒測試。
相信經過這個NIT_LIST_HEAD函數的測試用例,你已經初步創建起了對單元測試的印象。可是千萬不要覺得單元測試僅此而已,這是我刻意簡化的結果。要完整地掌握單元測試,還要好好學習一段時間。
目前,對於這個小小的單元測試案例,還有不少的不足,下面簡單羅列了幾項:
若是對於每一次檢查都採起直接寫if語句的形式,將形成大量的冗餘代碼,而且測試用例的編寫效率也會很低。
經過觀察程序是否返回0或者是-1的方式來判斷全部的測試是否經過並不直觀,一旦出錯也沒法立刻判斷是那一步測試出了問題。毫無疑問,咱們須要更加直觀的方式來展現哪一步成功或者哪一步失敗。
一份嚴謹的測試用例,會有大量的斷定。若是一個測試程序存在100次斷定,其中出現了3次失敗,那最終顯示一個百分比的測試經過率會比較直觀,好比能夠顯示97%的測試成功了。
後面會進一步介紹如何本身搭建一個簡單實用的單元測試框架,來解決上面這些問題。也會陸續展開介紹mock方法、打樁、錯誤注入、代碼覆蓋、動態分析、靜態分析、性能優化等內容。
03
總結
正如不少其餘技巧,好比打桌球、滑雪同樣,測試驅動開發也要花費至關長時間來練習。許多開發者已經接受了這種技術,並且不再想回到從前「後期調試式編程」的方式去了。
它會使你的代碼:
產生的bug更少
調試時間更短
徹底能夠經過提交你的單元測試案例,來證實你的項目可靠性。
1.第二屆國產嵌入式操做系統技術與產業發展論壇最新議程新鮮出爐!
6.重磅,傳AMD 300億美圓洽購賽靈思!最先下週達成交易
免責聲明:本文系網絡轉載,版權歸原做者全部。如涉及做品版權問題,請與咱們聯繫,咱們將根據您提供的版權證實材料確認版權並支付稿酬或者刪除內容。