淺談軟件測試嵌入式單元測試技能
單元測試是在軟件開發過程中要進行的最低級別的測試活動,在單元測試活動中,軟件的獨立單元將在與程序的其他部分相隔離的情況下進行測試。 單元測試不僅僅是作為無錯編碼一種輔助手段在一次性的開發過程中使用,單元測試必須是可重復的,無論是在軟件修改,或是移植到新的運行環境的過程中。因此,所有的測試都必須在整個軟件系統的生命周期中進行維護。
對嵌入式系統軟件執行測試是一項很有挑戰性的工作。因為嵌入式系統的軟件開發平臺和最終運行平臺是完全不一樣的兩個平臺,開發者不可能在其運行平臺上像在桌面環境那樣執行測試程序。這些差異可以表現在這多個方面。在軟件開發的時候目標硬件平臺可能還沒有準備好,軟件工程師在訪問硬件時可能會很麻煩,在開發環境下模擬整個系統存在困難性等。
本文闡述了單元測試在嵌入式系統軟件開發流程中的作用以及其如何幫助開發者處理上述疑問。簡要地講,它提升了樁函數在宿主環境下或者仿真器中的作用。這使軟件工程師在代碼編寫完畢之后就可以立即對其執行 驗證,即使此時目標硬件系統還沒有準備好或暫時不能執行測試。這樣,絕大部分的潛藏在程序中的邏輯疑問都能夠在早期被發覺,軟件工程師就可以迅捷地修正這些疑問,而目標平臺上的測試就可以注重于檢測軟硬件接口方面的疑問。
為何要對嵌入式系統軟件執行單元測試?
單元測試是檢查嵌入式系統軟件缺陷的最有效的一種要領,更確切而言也即在宿主機環境下或在仿真器上執行 的API測試。這能使測試盡早開始并且最大程度地降低了測試工作對目標硬件平臺的依賴性。嵌入式系統軟件的單元測試的一個前提是能夠讓軟件獨立于硬件執行 測試,這是由樁函數對目標硬件平臺的模擬而實現的。在這種情況下,代碼的絕大多數功能性測試都能夠獨立于目標硬件系統執行 測試。這對嵌入式系統開發者而言有以下主要優點。
1. 單元測試能讓開發者在目標硬件平臺準備好之前就開始測試周期,開發者可以直接在宿主開發環境下開始原始測試(而不須要目標硬件平臺)。早期測試能夠為開發團隊發覺并修正缺陷提供盡可能充足的時間。此外,早期測試還能將測試工作較平均地分配于產品開發的各個階段中,從而防止將所有的測試工作留到產品揭曉之前才執行 ,為開發團隊爭取了充足的時間。
2. 單元測試采取一種“各個擊破”的策略,允許用戶將復雜的系統分為相對較基本的準獨立系統執行 測試。測試工具能夠管理模塊之間的關聯性并運用樁函數來模擬這些模塊的行為。
圖1:運用樁函數,開發者可以模擬真實環境和程序之間的相互影響對程序的某個模塊執行 測試,而不須要目標硬件平臺以及暫時還未完成的其它代碼模塊。在測試環境中,樁函數充當了為待測模塊提供外部關聯性橋梁的作用。
在代碼修改流程中保衛代碼完整性
復雜系統的開發者最大的煩惱就是不能確定對代碼的修改能不能改動或破壞了程序的既有功能性。為了打消這些顧慮,用戶可以建立一個基準單元測試套件來捕捉代碼的既有功能性。如果用戶希望檢查相對于基準單元測試套件的改動,只須要對修改過的代碼定期地運行該測試套件即可。因為單元測試能夠獨立地測試系統的某些代碼,所以這樣一組回歸測試套件可以持續地執行而無論目標硬件平臺準備好與否。這種測試并不排斥對整個程序執行 測試的分離回歸測試套件。
這樣的測試套件作為變更檢測完全能夠讓用戶確保如果無意間破壞了既有功能性能夠被立即告知。當目標硬件平臺準備好后,用戶可以直接用宿主環境下的測試來驗證代碼能不能會在實際的目標硬件平臺上正確地運行。雖然代碼在宿主環境下執行 了回歸測試,但是在目標硬件平臺上還是須要執行 一定的自動系統測試。
驗證不正確處理
由于消費類產品的不正確處理可靠性需求的不同,所以系統測試情景變得更加復雜了。因為不能精確地預測其運用情況,所以系統的設計和測試都不能基于某個特定的情況。
相反這些系統必須經過廣泛地不正確以及未預期輸入的驗證,以保證這些輸入能夠被正確地處理。
運用含有樁函數的單元測試同樣能夠大大地簡化不正確測試流程。總的而言,在運用 程序這一層級來測試不正確條件是一件相當耗時的事,因為將程序置于“正確的不正確狀態”須要準備相對復雜的輸入數據并且同時要求程序在其大量的可能狀態中處于相應的狀態。與之形成鮮明比較的是,運用 “不正確模擬”要領來對某些函數執行 不正確處理測試則要基本得多。
例如,測試一個含有輸入數據不正確處理機制的測試是相當基本的:
- float signalToNoiseRatio (float signal,float noise,MODE mode)
- {
- if (MODE_MEASUREMENT == mode)
- {
- …
- if (signal < 0 noise < 0)
- {
- handle_bad_data();
- }
- }
- }
- float signalToNoiseRatio (float signal,float noise,MODE mode)
- {
- if (MODE_MEASUREMENT == mode)
- {
- …
- if (signal < 0 noise < 0)
- {
- handle_bad_data();
- }
- }
- }
在這種情況下,在測試程序中對handle_bad_data()函數的調用是很基本的,因為測試該語句能不能受程序輸入數據控制是很基本的。
盡管如此,很多情況下控制語句并不直接受控于函數接口,而是取決于系統處于的特定狀態,如下例所示。將系統置于該不正確狀態可能會相當復雜,甚至可能牽扯到讓設備接口處于某些特定狀態,所以測試用例中的這個條件就須要通過模擬來實現。
- float shutDown ()
- {
- if (uploadingData())
- {
- userMessage (“Cannot execute shutdown while uploading data”);
- recoverShutDown();
- }
- else
- {
- // shut down indeed
- }
- }
運用支持“智能樁函數”(smart stubs)的高級測試工具,對于復雜不正確條件的測試比其它要領就基本了很多。“智能樁函數”允許通過原始函數的樁函數以及通過實現某些必要的特定功能的要領來執行代碼。實際而言,當被測程序明顯沒有處于不正確狀態時,其相應待測函數將被調用以模擬不正確實際發生的情況,這就是“不正確模擬”的意義。在上述例子中,對不正確處理函數uploadingData()的測試須要其樁函數以保證至少讓其返回一次“真”。
一些值得考慮的疑問
在宿主環境下執行 測試可能意味著用以建立代碼的編譯器和目標系統編譯器不同。如果交叉編譯器提供商同時提供用在宿主環境下的編譯器(例如Green Hills Software的本地編譯器),則可以直接運用。如果沒有,則可以運用支持大多數平臺的GNU Compiler Collection(GCC)。雖然保證代碼在宿主編譯器和目標編譯器上的一致性會對維護成本有些許的提升,但是與早期測試對整個項目帶來的優點相比卻是很合算的。
單元測試不大可能發覺因同步不正確造成的運用 程序級的不正確條件或者與實際設備接口的不正確。盡管如此,在嵌入式系統的開發流程中,單元測試能幫助開發者盡早地發覺很多類不正確,所以提高了系統整體開發效率并且消除了測試瓶頸。
運用 C++test來自動化單元測試流程
用戶可以運用 Parasoft C++test來自動化對嵌入式系統軟件的測試。
Parasoft C++test是一套經廣泛驗證的最佳編碼實踐的自動化處理方案,它能有效地提高軟件開發團隊的開發效率以及軟件的質量。C++test能幫助用戶執行 編碼策略增強、靜態代碼分析、徹底代碼走查以及單元和組件測試,從而為用戶保證其C以及C++代碼按預期運行提供一個實際可行的要領。C++test可以在桌面環境下的主流IDE(包括Wind River Workbench以及Eclipse)以及回歸測試流程中的命令行中以批處理的方式運行。C++test集成了Parasoft的GRS報告系統,它能提供一個基于Web交互界面的報表并為用戶提供向下挖掘(drill-down)功能,基于C++test報告的這些結果,開發團隊實時把握項目狀態和趨勢以及其它關鍵指標。
對于嵌入式以及交叉開發而言,C++test可以在基于宿主環境和目標環境下執行代碼分析以及數據流分析。在宿主環境中,開發者可以通過C++test的編碼策略增強、靜態代碼分析、徹底代碼走查以及單元和組件測試模塊來對代碼執行 “隨時測試”式的驗證以及回歸測試。被測代碼的外部依賴性被樁函數自動地取代,樁函數能真實地模擬硬件以及其它代碼在實際運行中的表現。
圖2:Wind River Workbench中的C++test插件能為其用戶提供一個簡便的要領來運用 C++test執行 完整的代碼分析和單元測試。
通過擴展的基于宿主環境的測試,C++test能讓用戶在某個模塊代碼完成后立即對其執行 測試,即使此時目標環境還未準備好或不能執行 測試。這樣,絕大部分的潛藏在程序中的邏輯疑問都能夠在早期被發覺,從而讓開發者修正這些不正確變得十分迅捷,而在目標硬件平臺上的測試則能更加注重于驗證軟硬件接口疑問。此外,基于宿主環境的測試相對于目標系統而言更加容易自動運行和維護,這使得用戶可以不用其它嵌入式開發工具就可以驗證相對平臺獨立的代碼正確性。
當開發者在仿真器或實際目標硬件平臺上執行 測試時,在宿主環境下生成和優化過的測試套件可以被重用來驗證軟件在目標硬件上的功能性。先前的樁函數這時可以用實際代碼或者系統接口來代替從而完成完整系統的測試,而不須要修改測試代碼。C++test同時還提供一個內建功能來自動捕捉執行測試輸出以及將其轉變成后續回歸測試時的“黃金”數據集。
C++test讓整個測試流程自動執行 ,包括測試用例生成、交叉編譯、部署、執行以及將測試結果(包括覆蓋率指標)導入GUI。測試可以通過GUI交互地執行 或者通過命令行自動執行,以及以批處理的方式執行 的回歸測試。在交互模式下,用戶可以對單個模塊或者一組代碼執行 測試,從而讓調試和驗證都變得更基本。在批處理模式下,測試可以針對用戶提供的代碼或者根據文件名字或在硬盤中的位置來執行 。
同時,C++test還支持將其執行順序執行 完全的用戶自定義。除了內建的自動化測試,用戶還可以引入自定義測試腳本以及通過shell命令將測試工具根據代碼具體結構和測試環境執行 自定義。C++test的運行庫也可以被自定義并且針對不同的目標操作系統執行 交叉編譯。這種無可比擬的靈活性能夠任意實現其希望的測試流程而不須要預設工具的功能。
圖3:C++test的可自定義工作流程讓用戶可以根據代碼的構造來執行 測試,然后運用這個測試套件在目標硬件環境下測試其功能性和可靠性。
嵌入式開發支持的單元測試
真實的單元(函數/類)和組件測試
自動生成C或C++格式的測試套件
支持數據源
單一測試或者任意測試的任意組合方式的交互執行
捕捉實際測試結果后自動生成回歸測試套件
宿主平臺和目標平臺的統一測試環境
測試流程完全可自定義并提供運行時支持
語句、代碼塊、分支/條件以及路徑指標覆蓋率分析
提供HTML和XML格式的測試結果報告
支持GUI/桌面環境以及命令行方式
支持的目標編譯器
Wind River GCC 3.4.x和DIAB 5.4+
GCC 2.95.x – 4.1交叉編譯器
Green Hills 4.0.x
【編輯推薦】