軟體測試認識中的誤區 (轉)

amyz發表於2007-08-15
軟體測試認識中的誤區 (轉)[@more@]

測試認識中的誤區

摘要
由於人們對於軟體質量的重視程度越來越高,就導致了測試在中的地位越來越重要。是的,測試是目前用來驗證軟體是否能夠完成所期望的功能的唯一有效的方法。在這一趨勢的引導下,現在很多軟體相關的公司都非常重視對於他們所開發的軟體的測試,甚至不惜花費巨資購買商用的測試工具,但是效果卻不一定理想。究其原因主要是存在著對於軟體測試的諸多誤解。本文試圖對一些比較普遍的關於測試的誤解進行剖析,並且在測試對於軟體產品質量可能帶來的更深遠的影響方面,也進行了論述。

引言
測試在軟體開發過程中一直都是備受關注的,即使在傳統的軟體工程中,也有一個明確、獨立的測試階段。隨著軟體危機的頻頻出現以及人們對於軟體本質的進一步認識,測試的地位得到了前所未有的提高。測試已經不僅僅侷限於軟體開發中的一個階段,它已經開始貫穿於整個軟體開發過程,人們已經開始認識到:測試開始的時間越早,測試的越頻繁,所帶來的整個軟體開發成本的下降就會越多。Extreme Programming更是把測試推到了極限的位置,一切軟體開發活動都要從首先編寫測試程式碼開始。

但是,相對於測試這個詞的流行程度而言,有關測試教育方面的工作做的還遠遠不夠,很多關於測試的文章都是針對某種測試工具使用方面的,而測試工具廠商也往往出於商業的目的對於測試工具的作用誇大其詞。這樣,很多的軟體從業者就很容易陷入一些誤區,導致了測試並沒有在他們所在的軟體開發專案中起到有效的作用。下面幾個小節將對於一些比較具有代表性的誤區進行剖析,並對於測試背後所蘊含的一些設計思考進行了闡述,希望能夠起到拋磚引玉的作用。

誤區之一:使用了測試工具,就是進行了有效的測試

這個誤區可以說是一種通病,幾乎每一個領域中的CASE工具剛剛出現時都會帶來這個問題,比如:如果一個軟體開發團隊在軟體的開發中使用了Rational Rose工具來進行UML圖的繪製,他們可能就會聲稱他們採用了面向的方法,也不管他們的設計和程式碼實際上是多麼的過程化。

在測試領域中也一樣如此,一個軟體開發團隊往往認為只要自己使用了某種軟體測試工具,那麼就應該可以獲取測試帶來的種種好處,這種想法當然是錯誤的。因為,要想對一個軟體或者模組進行有效的測試,首先該軟體或者模組應該是可測試的。可測試性是反映軟體質量的一個內在屬性,不會因為你使用了某種測試工具進行了測試行為,就使得被測試的軟體具有了可測試性。如果被測試的軟體本身並不具備可測試性,那麼使用多麼昂貴的測試工具進行測試所能夠帶來的收益都是微乎其微的。

巧的是,可測試性和好的軟體品質的其他方面有天然的關聯,一個具有可測試性的軟體必然是一個強內聚、弱耦合、介面明確、意圖明晰的軟體,而不可測試的軟體往往具有過強的耦合和混亂的邏輯。關於可測試性方面更多的內容不在本文的論述範圍,請自行參見相關的文獻(本文所附的參考文獻中有關於可測試性的更深入的資訊)。

要想真正獲取測試帶來的巨大好處,並且使得測試工具能夠發揮最大的,關鍵就是要使軟體本身具有很好的可測試性。這種能力的獲取是一個逐步的過程,是不可能一蹴而就的。最關鍵的一點就是要不斷實踐,不斷學習一些優秀的,不斷的反思。要想獲取好的結果,就必須要付出努力,這是亙古不變的真理。Extreme Programming中的測試先行的實踐倒是一個很好的起點,具體可以參見參考文獻[3]。

對於測試工具的選擇,只要滿足需要並能夠自動執行測試用例就可以了。不要一味的追求複雜的功能和不必要的靈活性。對於大多數專案來說,一些非常著名的原始碼開放的測試工具就足夠了,比如:方面的單元測試工具JUnit和C++方面的單元測試工具CppUnit。關於驗收測試方面,目前沒有比較好的滿足各方面需要通用的測試工具,不過使用指令碼語言,循序漸進的自行開發一個適合自己的驗收測試工具也不是一件困難的事情,一句話:只有提高了自身團隊內在的素質,外在的工具才能夠發揮作用。

誤區之二:存在太多的無法測試的東西

在軟體開發領域,確實存在一些東西看起來要比另外一些東西難測試一些,但是遠非無法測試。並且在大多數的情況下,還是由於被測試的軟體本身在設計時沒有考慮到可測試性的問題。只不過這種不可測試性不是由於被測試的軟體內部的過緊耦合造成的,而是和外部某些很難測試的部分耦合過緊,從而表現出被測試的軟體本身很難測試。這些很難測試的部分比較常見的有:圖形介面、、等。下面以圖形介面為例進行說明。

 如果一個軟體模組必須要透過圖形介面才能夠觸發它的應用邏輯時,那麼要對這個軟體模組進行測試時就必須要使用圖形介面。但是圖形介面又是很難測試的。看起來好像很難辦。讓我們換一個角度考慮一下,其實我們真正想要測試的是軟體模組本身的應用邏輯,而不是圖形介面的觸發邏輯。如果我們在設計時能夠把被測試的軟體模組本身進行很好的封裝,定義良好的服務提供介面,那麼我們就完全可以透過軟體模組本身提供的介面進行測試。這樣就可以繞過難以測試的圖形介面。造成上述軟體模組很難測試的原因正是由於在設計時把軟體模組本身的應用邏輯和圖形介面這兩個無關的邏輯耦合在了一起。把這兩個邏輯分離,不僅可以對該軟體模組進行更容易、更有效的測試,而且也使得該軟體模組具有了很好的可重用性和可移植性。

那麼對於不好測試的圖形介面,我們該怎麼辦呢?原則很簡單,如果某種東西不好測試,那麼就讓它做肯定不會出錯的事情,而把可能會出錯的邏輯剝離出來,放到一個可以測試的模組中。對於圖形介面來說,就是僅僅保持一個很薄的圖形介面邏輯,它的工作就是把的請求簡單的轉發給真正處理該請求的軟體模組(一般稱之為Application Facade)。轉發邏輯足夠簡單以至於它肯定不會出錯,所以我們也無需對它進行測試。關於這方面更多的資訊,請參見參考文獻[5]。

如果在進行軟體開發時能夠首先編寫測試程式碼,那麼就會迫使你從易使用性,易測試性的角度開考慮問題,從而你就會專注於軟體模組的高層抽象和職責。這樣就會定義出清晰的、明確反映意圖的模組介面來,另外,為了使得測試能夠進行,你就會主動把那些導致不好測試的耦合去掉。這樣的結果不僅僅是獲得了可測試性,並且也產生了更好的設計和架構。

但是確實存在一些不好測試的東西並且很難只讓它執行一些非常簡單的邏輯,比如嵌入式系統中的BSP(板級支援包)。開發它們所使用的語言、環境以及它們本身的特性等決定了它們是很難測試的。這裡說的難測試其實是一個測試代價問題,具體的說就是要付出更多的時間和努力。這就需要你在付出的代價和測試帶來的收益之間進行平衡。如果付出的代價所帶來的收益(更少的、維護、釋出成本)是巨大的,那麼付出的代價就是非常值得的。

誤區之三:測試程式碼可以隨意寫

 大家肯定知道測試程式碼是不能隨意編寫的,並且在編寫測試程式碼時也不是抱著一種隨意的態度,但是你編寫出來的測試程式碼以及測試程式碼執行的情況卻表現出了一種隨意性和無序性。因為你可能並沒有弄清楚測試的真正意圖所在。

 本人曾經看到過有關驗收測試的這樣一個案例,驗收測試者使用昂貴的商用測試工具對一個具有圖形介面的軟體進行測試。測試的方法是透過編寫測試指令碼滑鼠在圖形介面上隨機的點選(當然每一次的點選,都點到了圖形介面上可以接收事件的區域),然後等待著被測試軟體的崩潰。當然這種測試方法可以作為驗收測試的一個方面,但是決不是唯一的一個方面。還有更重要的內容被忽視了。

 測試的目的是用來檢驗軟體系統是否滿足了需求。所以,你的測試程式碼一定要明確的表達出這一點來。就那上面的案例來說,如果測試者真正從使用者的需求出發,那麼他寫出來的測試指令碼肯定不會是那樣的,而因該是每一個測試用例都清晰的刻畫了一項使用者的需求,然後檢驗系統是否實現了使用者期望的功能。這樣的測試才是有明確目的,才是最有效的測試。和把介面邏輯和應用邏輯隔離,採用明確表現使用者需求測試用例進行測試相比,上面的測試方法不能不說是隨意了一點。

 在針對類進行的單元測試中,也經常會看到一些錯誤的測試方法。很多的測試者往往針對類中的每個特定的實現細節進行測試。類中的特定的實現細節是很容易變化的,在的迭代式開發中更是如此。一旦你測試的類中的某個實現細節發生了變化,你原先的測試程式碼就要進行相應的更改,否則就失去了意義。這種頻繁更改的代價是巨大的。類是一種抽象,它反映了更高層面的內聚性,它應該有明確的職責和定義良好的服務介面,它的職責和對外的介面相對於內部的實現細節來說要穩定的多,並且我們要驗證的正是這個類是否具備了它的職責。因此,在對類進測試時,我們應該針對類的介面來驗證類是否實現了它的職責而不是其他。這樣寫出來的測試程式碼要穩定的多,也有效的多。

 細想一下,造成容易陷入針對實現細節測試的原因主要是由於先實現了類,然後才去進行測試。如果先實現了類的功能,然後在去對類進行測試,潛意識中就會不由自主的想去驗證已經完成的類的某種實現細節。如果能夠在編寫實際類前,首先編寫針對該類的測試程式碼,情況就會有很大的不同,因為這會迫使你從類的使用者的角度去考慮問題。結果就是會把關注點放在類的易用性上,放在類的職責上面,放在類提供服務的介面上面,而不是某種實現細節。
 總之,測試程式碼的編寫應該從被測試的物件是否滿足需要的層面進行,而不是其他。

誤區之四:單元測試和驗收測試沒有什麼區別

和誤解之三一樣,可能很多人並不承認這一點。但是他們卻又不能比較清楚的說出二者的差別來。這樣,在他們進行測試程式碼的編寫時就會比較迷茫。本小節結試圖給出一些區分單元測試和驗收測試的一些原則來。

 我們還以經常用來和軟體進行類比的建築為例,首先給大家一個感性的認識。單元測試可以類比為一個建築的質檢人員對建築進行的檢測, 他關注的重點是建築的內部結構、地基、以及牆壁是否垂直等。他的檢測是要保證建築的各個部分是正常的、的,換句話說,就是要保證施工滿足建築上面的質量標準。驗收測試可以類比為建築的使用者來對建築進行的檢測。首先,他認為這個建築是滿足規定的工程質量的,這是有建築的質檢人員來保證的。使用者關注的重點是住在這個建築的中的感受。他關心建築的外觀是否美觀、各個房間的大小是否合適,窗戶的位置是否合適,是否能夠滿足家庭的需要等。這裡,建築的使用者執行的就是驗收測試,他是從使用者的角度出發的。建築的質檢人員執行的就是單元測試,他是從構建者的角度出發的。

 正是這種角度的不同決定了單元測試和驗收測試之間的區別。它們是對系統的不同的方面進行的測試,二者是互相補充的。不管我們在系統的構建中使用了多麼聰明的方法,不管我們的系統是多麼的靈活,但是首先我們的產品必須是可用的,否則我們所做的就是浪費時間,從這一點上來說驗收測試要比單元測試顯得更加重要。

還以上一小節給出的案例為例,案例中所使用的測試方法僅僅是從系統是否健壯的角度出發進行的,即使系統從不崩潰也不能證明那是一個可用的系統。因為測試根本就不是從使用者使用的角度出發的,測試者本應該和使用者一起來編寫驗收測試。單元測試保證我們把事情作對,而驗收測試則保證我們做正確的事情。

 關於單元測試和驗收測試之間的明確劃分,沒有一個通用的標準,只有透過自己的不斷實踐來增加這方面的經驗。你進行的有關測試的實踐越多,你就會越清晰的感覺到單元測試和驗收測試之間的界限所在。下面給出一些指導原則,在你編寫測試程式碼時可能會有幫助。
 如果一個單元測試要跨越類的邊界,那麼它可能應該是一個驗收測試
 如果一個單元測試變的非常複雜,那麼它可能應該是一個驗收測試
 如果一個單元測試經常要隨著使用者需求的變化而改變,那麼它可能應該是一個驗收測試
 如果一個單元測試比它要測試的程式碼本身要難以編寫,那麼它可能應該是一個驗收測試

結論
  測試是用來保證軟體開發過程的高效性,以及保證開發出來的軟體產品的高質量和可用性的。軟體開發本身就是一件非常困難的事情,這也決定了有效的測試決不是簡單的依靠一些測試工具就可以進行的。在使用工具的同時,我們更要加強關於測試的培訓、教育,使大家對於測試首先有一個正確的認識。只有這樣,我們才能夠更加有效、高效的使用工具,才能夠使測試真正起到它應有的作用。希望本文能夠對大家在進行測試方面的工作時有所幫助。

參考文獻
[1]  Extreme Programming Explained: Embrace Change,Kent Beck,1999
[2]  Agile Software Development, Principles, Patterns, and Practices,Robert C. Martin,2002
[3]  Test Driven Development: By Example ,Kent Beck, 2002
[4]  Refactoring:  Improving  the  Design of Existing Code,Martin Fowler, Kent Beck,1999
[5]  Testing Things That Seem Hard to Test,Robert S. Koss ,2001

 


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752019/viewspace-958099/,如需轉載,請註明出處,否則將追究法律責任。

相關文章