2007年10月31日 星期三

F23 測試結果與對應文件裝訂在一起

需求被定義為功能,功能經由設計而實現,實現的程式藉著測試來保證運作的正常。不止是測試的結果,這一系列的相關文件具有演進的垂直關係,理應放置在一起以便查詢。

不過物件的位置與關係至少會是二維的,處於同一層次的物件彼此間會有相互使用的水平關係。像最開始會在功能裡使用其他功能,接著就影響到其下的設計與測試,都必須等待自己使用的功能完成後才能開始。這樣的關係也是必須在文件裡註明的。

另外也要記得同種類物件的清單必須產生,這是各個層次物件的快速索引表。如果有能力的話,水平追溯表與垂直追溯表還是做出來會比較方便查出所有的關聯;但是如何與真實物件間的關係作同步的修改,將會是日後需要多花資源的地方。

總之,讓同一個功能之下所有項目的資料都放在一起,這是符合聚合並封裝概念的作法。

2007年10月30日 星期二

F22 追溯關係(4)──Use Case vs. Test Case

Use Case應該與Test Case作好關聯的追溯。我們可以藉由測試過哪些Test Case來達到保證某特定Use Case可以正確執行的結論。

不管是因為什麼原因更動了程式碼,最基本的就是那個Class必須重作Unit Test;其次再依序往上,一層層地對使用到這個Class的Class作Unit Test,並依此原則遞迴測試所有最終會用到該Class的所有程式。

接著需要分析的是剛才所有重新作過Unit Test的Class,各自被包含到哪些Use Case的範圍內;有包含那些Class的Use Case對應的Test Case都必須重新進行測試。如果更動的是很多Use Case所使用的核心Class,那麼重新測試耗費的資源會是很可觀的。

要能根除“改過這裡之後要測試哪些東西才能再度保證系統是可以正常運作的?”這樣的問題,唯有作好對應功能、設計與相關測試的追溯關係方有終點。

2007年10月29日 星期一

F21 測試錯誤的處理(3)──迴歸測試(Regression Test)

如果問題單提出的內容的確是系統的錯誤,那麼系統理應針對這個問題作修正。程式的任何變動都會造成程式所在方法的改變,進而影響到呼叫該方法的所有地方。為了保證一段程式被修正後,其他使用它的方法同樣運作正常,我們必須再重新測試過這些方法所對應的測試項目(即使修改前已經全部測試通過)。

由此可見,程式的變動是需要審慎從事的。在國外的專案裡,每一行程式的變動,客戶都會詢問是為什麼而改,同時會影響哪些範圍,因為錯誤的解決方法會讓系統如同骨牌傾倒般到處出現連帶的問題。有時邏輯上的問題造成兩個地方糾纏不清,A改好B就有問題,去改了B時A又壞掉,這樣的Side Effect是任何人都沒法忍受的。

前面提到程式的追溯最好是到方法層級,這是因為方法內部的改變只要往上追溯使用該方法的地方;如果追溯的程度使用的是類別,那會使得追溯出來的範圍過大且不正確。涵蓋了一堆沒有使用關係的方法,會測試到一堆本來就正確的地方(因為沒用到改變的地方),比起在設計時去追溯方法的關係,將會浪費更多的測試資源。

2007年10月28日 星期日

F20 測試錯誤的處理(2)──用Defect System記錄

問題單應該統一開立在一個地方,至少可以使用一個Excel存放;但是最理想的方式還是使用Defect System。後者的好處是可以集中存放,多人同時使用與編輯,而且在與自己有關的問題單改變時,會主動發出郵件通知(Event)而不用時常進入系統查看。

把開立的問題單視為資料庫,我們可以在特定時間點統計開立問題單的數量、退回數量、解決數量、未解決數量、重開問題數量……等等的統計與百分比,這些數據的統計範圍除了整個系統之外,也可以針對各個定義的範圍(模組或功能)統計出各個部件的問題單狀況。

另一個好處是,任何時候遇到系統上的問題都可以先到Defect System上查詢以前是否發生過同樣的狀況,如果有的話先參考之前是如何解決或是如何正確使用的,如果沒有就能放心地開立新的問題單而不用擔心會有重覆的狀況。

類似的系統在系統出版本後,可以應用在FAQ(答客問)的方面,使用者遇到問題或是不會使用的狀況,都能找到一個集中管理類似問題的資料庫搜尋想要的資訊或提問讓系統維護人員回答。

2007年10月26日 星期五

F18 Unit Test(4)──測試Component Interface

表面上Component Interface要測試Interface,但是事實上同樣是要測試實作Interface的所有Class。

測試時每個實作Class生成一個測試類別,每個Component Interface方法都對應一個測試方法,這與Interface的測試策略相同。從Class到Interface到Component Interface逐步的測試,代表的是由組成單位往上組裝成元件的測試,經由一層一層的設計與測試確認,我們得到的將會是穩定度很高的元件。

元件是用來組成系統的最大單位,在元件之上就是系統使用它們的控制流程;測試控制流程的部分就進入到Use Case相關的功能測試與整合測試。參考系統架構設計的圖,單元測試主要是保證的是右下角元件的正常運作,整合測試、功能測試與使用者測試則是針對其他與專案相關的層次加以測試。

註:專案層級裡所有程式同樣也需要通過單元測試,先保證每個Class的方法都正確後才可以進行更上層的測試。

2007年10月25日 星期四

F17 Unit Test(3)──測試Interface

在元件內部的設計層次我們的架構留了幾個介面的存在,介面之下的實作少則一個Class,多的話也有可能超過十個。無論如何,Class測試通過後就要往再上一層的元件內部介面進行測試。

每一個元件內的介面,其往下的實作Class會生成一個測試類別,每一個介面裡定義的方法都生成測試方法加以測試。測試的原則同樣以在時程允許下寫出最多的測試內容為理想作法。這裡進行的已經是小規模的Class整合,有時明明每個Class都測試通過,但是放在一起就是沒法全部測過。就像有時社會裡明明每個人都守分地只做自己應做的事,但是整個社會還是會莫名其妙地產出問題。

Interface層級的測試,是把它之下從實作的Class起的使用關係都視為封裝的組件,測試通過保證這一部分的程式都沒有問題後,才允許再往上一層的元件使用這些層級來組合達成元件預期提供的功能。

註:Class與Interface的測試都要依據使用關係,從沒有使用其他Class或Interface的開始測試起,依序照著使用關係往上測試。

2007年10月24日 星期三

F16 Unit Test(2)──測試Class

Class是系統組成的最小單位,因而同樣也是單元測試的最小單位。設計時讓每個Class都各如其分地負責自己的工作,測試時則確保每個Class都能正常運作,這麼一來採用這個Class組成的大小組件就不會出現基本層面的問題。

一個Class會有一個對應的測試類別,生成的同時應針對public、package與protected的方法產生對應的測試方法,每個測試方法裡依設計規格產生輸入用的資料後,呼叫該方法並驗證傳回值是否正常或是否如預期般拋出例外。public的方法是必測的,package與protected的方法如果可以測試會更理想,然而因為後兩者在包裝層次與元件時可由外部測試來涵蓋相當大的部分,要是時間不夠的時候略過也無妨。

能夠針對規格裡提到的所有狀況都寫出一段測試程式是最理想的,因為測試涵蓋度越大就越不容易有問題,不過測試時大多都只會挑選一些重要的規格,省略測試功能的結果就是有可能讓小問題在未來因為相互影響而成為更大的問題。

註:測試Class的範圍包含static APIs但不包含Abstract Class。

2007年10月23日 星期二

F15 Unit Test(1)──基礎的單元測試

程式的實作都在Class裡完成,元件與系統都是由Class所組成。確保Class提供給外部使用方法運作的正確,就是單元測試所要提供的保證。

程式實作的過程會根據Coding Standard來review所有的程式碼,寫作的風格也有檢查的工具,只要依希望的程式風格設定參數檔後,工具就會依設定檢查程式並條列出不合規定的所有地方。由於改變程式就會有影響,所以在測試之前應先進行review與調整到符合規定的穩定程度再進行單元測試。

Unit Test有測試工具可以使用,對於Class來說會有一個測試用的Class裡面有每個方法對應的測試方法,我們在初始化方法定義每個測試方法之前的動作後,即可在測試方法裡寫下所有的測試動作。工具允許把Class依所屬關係集合起來一次執行,並在結果上註明有哪些測試的動作沒有得到預期的傳回值。

Class本身的方法、元件層次Interface的定義、封裝元件的Interface、系統層次的Interface,由下往上組合而成的各個組件,每一層在測試時都會被視為獨立的黑箱,由Unit Test工具進行所有方法的結果檢查。