測試是傳統(tǒng)軟體開發(fā)的最后一步。整個軟體開發(fā)過程,需要收集要求、進行高層次的設計、詳細設計、製作程式碼、進行部份單元測試,然后整合,最后才開始最終測試。
最佳的開發(fā)實踐應包含程式碼檢查這個步驟。然而程式碼檢查一般只能找出70%的系統(tǒng)錯誤,因此完美的測試環(huán)節(jié)絕對必不可少。測試就像個復式記帳系統(tǒng),可以確保將缺陷扼殺在最終推出的產(chǎn)品之前。
在所有其它的工程實踐中,測試都被視為基本環(huán)節(jié)。比如,在美國,每一座聯(lián)邦政府出資修建的橋都必須經(jīng)過大量的風洞測試。而在軟體領域,測試并沒有很受重視。儘管測試是所有工程實踐準則的關鍵部份,但編寫測試程式卻感覺是在浪費時間。好在嵌入式系統(tǒng)設計界內的許多領域已經(jīng)將測試作為其工作的核心部份,他們認識到將這個關鍵步驟放在計畫末期極不明智,因而主張同步地編寫測試程式和應用程式。
嵌入式系統(tǒng)軟體測試在諸多方面都與應用軟體測試一樣。不過,應用測試與嵌入式系統(tǒng)測試之間還是存在一些重要差異。嵌入式開發(fā)人員一般會用到基于硬體的測試工具,而這類工具通常不會用于應用開發(fā)過程中。此外,嵌入式系統(tǒng)一般都有些獨一無二的特性,這些特性應該在測試計劃中得以體現(xiàn)。本文將介紹測試和測試案例開發(fā)的基礎知識,并指出整個嵌入式系統(tǒng)測試工作的特有細節(jié)。
何時測試以及如何測試
從圖1可以看出,在可行的條件下,測試應盡早展開。一般來講,最早的測試是由最初的開發(fā)人員進行的模組或單元測試。遺憾的是,開發(fā)人員大多對如何建構一整套測試例程以進行測試所知不足。由于精心設計測試例程通常直到整合測試時才能使用,因此許多在單元測試過程中就能找出的缺陷直到整合測試時才會被發(fā)現(xiàn)。比如,硅谷的一家大型網(wǎng)路設備廠商為找出其軟體整合問題的關鍵塬因,進行了一項研究。這家廠商發(fā)現(xiàn),在計畫整合階段找出的缺陷中,有70%是由在整合之前從沒被執(zhí)行過的程式所產(chǎn)生的。
圖1:改正問題的成本。
單元測試:開發(fā)人員在單獨進行模組級測試時一般是編寫存根程式碼(stub code)取代余下的系統(tǒng)軟硬體。在開發(fā)週期的這個環(huán)節(jié),測試主要側重于程式碼的邏輯性能。
通常,開發(fā)人員會分別使用某些平均值、高值或低值、以及某些超出範圍的值(以測試程式碼的異常處理功能)進行測試。但這些基于‘黑盒子’的測試僅能對模組中整個程式碼的一部份進行測試。
回歸測試:測試不應是一勞永逸的。每次修改程式后都應該重新進行測試,以確保這些更改不會無意中‘誤傷’某些不相關的行為。
稱為回歸測試的這類測試,一般是透過測試腳本自動進行的。比如,如果你設計了一組100個輸入/輸出(I/O)測試,回歸測試腳本會自動執(zhí)行這100個測試,然后將輸出與一組‘黃金標準’輸出進行對比。每次對程式碼的任何部份進行修改時,都要對包含被修改程式碼的整個程式執(zhí)行整套回歸測試程式包,以確維修改過程中不會‘誤傷’其余程式碼。
測試什么
因為沒有一個實際的測試集可以證明一個程式是正確的,因此關鍵問題變成了哪個測試子集最有可能檢測到最多的錯誤。選擇合適的測試例程的問題被稱為測試例程設計。雖然存在數(shù)十種測試案例的設計方法,但它們通常可歸為兩種截然不同的方法:功能測試和覆蓋測試。
功能測試(也稱為黑盒子測試)選擇可評估實現(xiàn)與需求規(guī)格符合程度的測試。覆蓋測試(也稱為白盒子測試)選擇可執(zhí)行程式碼某些部份的測試例程。(過后,將詳細討論這兩種方法。)
這兩種測試都是對嵌入式設計進行嚴格測試所必須的。其中,覆蓋測試表示程式碼的穩(wěn)定性,所以這種測試是用于已經(jīng)完成或將近完成的產(chǎn)品的。另一方面,可在編寫要求文件時,同時編寫功能測試。
事實上,從功能測試開始入手,可以大幅地降低重復勞動和重寫測試案例的工作。因此,在我看來,要先考慮功能測試。
每個人都同意先編寫功能測試這個觀點,有人認為,功能測試在系統(tǒng)整合階段(而不是在單元測試時)最有用。以下是整合功能測試和覆蓋測試方法的一個簡單處理流程:
1.找出哪些功能未被功能測試完全覆蓋。
2.找出每個功能的哪些部份沒被執(zhí)行。
3.找出需要哪些額外的覆蓋測試。
4.執(zhí)行新增的額外測試。
5.重復以上步驟。
何時停止測試?
最通用的停止標準(按可靠性排序)如下:
1.老板命令停止測試
2.新的測試週期找到的新缺陷少于X個
3.在沒有發(fā)現(xiàn)任何新缺陷的情況下已經(jīng)滿足了某個覆蓋閥限
無論你多么徹底地測試了程式,都無法保證找出所有缺陷。這引發(fā)了另一個有趣的問題:你可容忍多少缺陷?假設在極端軟體壓力測試過程中,你發(fā)現(xiàn)系統(tǒng)每進行大約20小時的測試就會鎖定。你仔細地檢查程式,但是仍無法找出這個錯誤的根源。這個時候你應該提供產(chǎn)品嗎?
多少測試才‘足夠好’?這個我說不好。但遵循一些久經(jīng)時間考驗的規(guī)則總是好的:“如果方法Z預估Y行程式碼中的缺陷少于X個,那么就可放心地發(fā)佈程式了。”也許有一天會出現(xiàn)這種標準。編程產(chǎn)業(yè)仍然相對年輕,還達不到類似建筑業(yè)那樣的成熟度。
許多厚厚的建筑手冊和大本規(guī)格是多年經(jīng)驗的結晶,它們可為建筑師、土木工程師和結構工程師提供按工期在預算內、建造一棟安全建筑所需的全部資訊。偶爾雖仍會有建筑倒塌,但畢竟很少見。在編程產(chǎn)業(yè)制訂出類似標準前,“多少測試才足夠?”就是個主觀判斷問題。
選擇測試案例
在理想情況下,你可能想要測試程式中每一個可能的行為。這意味著每一種可能的輸入組合或者每一種可能的判定路徑至少測試一次。
這是個崇高但完全不切實際的目標。比如,Glen Ford Myers在其《軟體測試的藝術》一書中就描述了一個只用五個判定條件就可有1014個不同執(zhí)行路徑的小程式。他指出,如果你能夠每五分鐘就能編寫、執(zhí)行并驗證一個測試例程的話,那么全面徹底地測試完這個小程式需要10億年時間。
顯然,理想的狀況是無法實現(xiàn)的,因此你必須採用接近這種理想狀況的標準。如你所見,功能測試與覆蓋測試相結合可以提供合理的次優(yōu)選擇方案。基本方法是選擇最有可能發(fā)現(xiàn)錯誤的測試(一部份功能測試,一部份覆蓋測試)。
1.功能測試
功能測試一般稱為黑盒子測試,因為在編寫功能測試的測試例程時并沒有涉及實際的程式碼。換句話說,沒有觸及到‘盒子內’。嵌入式系統(tǒng)有輸入和輸出,并在輸入和輸出之間執(zhí)行某些演算法。黑盒子測試是根據(jù)對哪些輸入應該是可接受的以及這些輸入應與輸出有何種關係的了解來進行的。黑盒子測試完全不了解輸入與輸出之間的演算法是如何實現(xiàn)的。黑盒子測試的示例包括:
壓力測試:有意使輸入通道、記憶體緩衝器、磁碟控制器、記憶體管理系統(tǒng)等過載的測試
邊界值測試:表示特定範圍內的‘邊界’的輸入(例如,對于整數(shù)輸入而言,是最大和最小整數(shù)以及?1、0、+1);以及應使輸出在輸出範圍的類似邊界出現(xiàn)跨變的輸入值。
異常測試:能觸發(fā)故障模式或異常模式的測試。
錯誤推測:根據(jù)以前的軟體測試經(jīng)驗或者從測試類似程式獲得的經(jīng)驗進行的測試。
隨機測試:通常,這是效率最低的一種測試方法,但卻仍然廣泛用于評估用戶介面程式碼的強韌性。
性能測試:由于性能預期是產(chǎn)品要求的一部份,因此性能分析屬于功能測試的範疇。
由于黑盒子測試僅取決于程式要求及其I/O行為,因此一旦完成功能要求的編寫,即可開發(fā)這類測試。這使得黑盒子測試例程的開發(fā)可以與余下的系統(tǒng)設計同步進行。
與所有測試一樣,功能測試應被設計得具有破壞性,也即,要試圖證明程式無法工作。這包括使輸入通道過載、隨意地敲打鍵盤,以及故意地做程式員認為會破壞其程式的所有事情。
作為研發(fā)產(chǎn)品經(jīng)理,這是我的主要測試方法之一。如果產(chǎn)品在經(jīng)過40個小時的極限測試(abuse testing)后,并沒發(fā)現(xiàn)任何嚴重或者致命的缺陷,那么就可以發(fā)佈這個產(chǎn)品了。如果找到了一個重大的缺陷,那么修正這個缺陷后,還必須重復前面的測試步驟。
2.覆蓋測試
功能測試的缺點是其很少執(zhí)行全部程式碼。覆蓋測試則試圖規(guī)避這個缺點,它採用的方法是(理想地)確保每一條程式碼語句、判定點或者判定路徑都至少被測試一次。覆蓋測試還可以顯示已經(jīng)存取的數(shù)據(jù)空間大小。
覆蓋測試也稱為白盒子測試或玻璃盒子測試,這類測試的設計需要全面了解軟體的實現(xiàn)方式,也就是說,它要‘看到盒子裡面’。白盒子測試利用了塬始程式碼所能提供的方便。
白盒子測試充分借力了程式員對程式API、內部控制結構的知識,分享了程式員的異常處理能力。由于白盒子測試取決于具體的實現(xiàn)決策,因此要到應用程式碼完成后,才能動手設計這類測試。
從嵌入式系統(tǒng)的角度來看,覆蓋測試是最重要的測試,這是因為只要你把握已在多大程度上對程式碼進行了測試,你就可很好地預警出現(xiàn)未發(fā)現(xiàn)缺陷的風險。白盒子測試的示例包括:
語句覆蓋:選擇的測試案例可以至少將程式中的每一條語句執(zhí)行一次。
判定或分支覆蓋:選擇的測試例程可以使每一個分支(條件為真和假的路徑)至少執(zhí)行一次。
條件覆蓋:選擇的測試例程可以強制判定中的每一個條件(項)都包含所有可能的邏輯值。
理論上,白盒子測試可以利用或控制所需的任何對象來執(zhí)行其測試。因此,白盒子測試可能使用JTAG介面強制設定特定的記憶體值作為測試的一部份。實踐上,白盒子測試可以分析邏輯分析儀報告的執(zhí)行路徑。
3.灰盒子測試
由于白盒子測試可以深入程式碼內部,因此與黑盒子測試相較,這類測試的維護成本更高。只要要求和I/O關係保持穩(wěn)定,黑盒子測試就會一直有效;但每次修改程式碼后,可能都需要重新進行白盒子測試。因此成本效益最高的白盒子測試一般是那些在不深入編程細節(jié)的情況下利用實現(xiàn)知識進行的測試。
較少涉及程式碼細節(jié)的測試有時也稱為灰盒子測試。當與‘錯誤推測’配合使用時,灰盒子測試非常有效。如果你知道(或者至少猜到)程式碼中的弱點在哪裡,那么你就可以設計出對這些弱點‘施壓’的測試案例。
因為這些測試覆蓋了程式碼的特定部份,因此這些測試是灰盒子測試;因為這些測試是根據(jù)可能會出現(xiàn)哪些錯誤的猜測而選擇的,因此這些測試是錯誤推測測試。
在整合新功能與穩(wěn)定的舊程式碼庫時,這種測試策略非常有用。由于程式碼庫已經(jīng)過全面的測試,因此將測試重點集中在新、舊程式碼交集處可以起到事半功倍的效果。
評論
查看更多