每一個找到的問題點都會有許多執行流程經過,完整找出經過這個問題點的所有執行流程並加以測試,是保證修改後所有功能依然正常的迴歸測試的任務。
修改某個Method之後,使用Eclipse的Preference功能可以找出上一層有直接呼叫的Method,根據找出的Method清單再向上找出更上一層的Method清單,再根據更上一層的Method清單往上找……。在沒有記錄的情況下往上找,可能就像從一大群同姓的宗親中找出自己的所有直系血親一樣困難,如果有系統裡有類似族譜的記載不是就容易得多了嗎?任何一個人都可以很快地找出自己的所有直系血親。
如果沒有做出自動測試的話,每次修改都要重作一次影響部分的Unit Test、功能測試、使用者測試……等,可以說改得越多就測得越痛苦。不管是時間因素或是人為因素,只要跳過沒測試的部分都可能在系統裡埋下了潛在的問題。以經驗來說,系統或元件都會是需要修改的,因此能夠節省時間的自動測試就相對變得重要。
測試是為了找出系統有問題的地方,但是進行測試是需要代價的。如果能夠讓測試自動地被進行,問題也自動地被找出來,那該是多麼理想的一件事?
2008年5月23日 星期五
2008年5月22日 星期四
P15 測試的過程(6)──多執行緒與重覆操作的壓力測試
在Client-Server架構下的效能測試還要包括多個Client同時存取一個Server時的壓力測試,用來驗證同一個交易在上百個Client同時對Server存取時是否同樣在執行時間的限制內正常地動作。
客戶會指定最少要在幾個Client下要能正常運作,我們的作法大致會讓一台電腦開啟10-20個thread同時發送交易電文到Server,然後多台電腦同時重覆對server發送交易電文,連續執行客戶指定的時數後沒有發生當機或是記憶體使用異常的現象才算過關。
重覆性的測試在UI的部分同樣也需要,因為使用者會在工作的時數內一直對UI作各式各樣的操作。理想的作法是用能錄下使用者鍵盤與滑鼠動作的Robot軟體,依序錄下指定交易的輸入流程(可包含故意輸入錯誤的操作),然後設定為重覆播放,這樣就可以對UI進行不斷的操作測試。
同樣地準備幾個不同的電腦用Robot執行不同的混合劇本,連續執行客戶指定的時數後若沒有發生當機或記憶體使用增加的狀況才過關。這個測試有時會被跳過,改由使用者測試時進行較多Test Case的操作來觀察是否有異常現象;不過這種替代方式的操作數比不上Robot而會有某些狀況無法測出的缺點。
客戶會指定最少要在幾個Client下要能正常運作,我們的作法大致會讓一台電腦開啟10-20個thread同時發送交易電文到Server,然後多台電腦同時重覆對server發送交易電文,連續執行客戶指定的時數後沒有發生當機或是記憶體使用異常的現象才算過關。
重覆性的測試在UI的部分同樣也需要,因為使用者會在工作的時數內一直對UI作各式各樣的操作。理想的作法是用能錄下使用者鍵盤與滑鼠動作的Robot軟體,依序錄下指定交易的輸入流程(可包含故意輸入錯誤的操作),然後設定為重覆播放,這樣就可以對UI進行不斷的操作測試。
同樣地準備幾個不同的電腦用Robot執行不同的混合劇本,連續執行客戶指定的時數後若沒有發生當機或記憶體使用增加的狀況才過關。這個測試有時會被跳過,改由使用者測試時進行較多Test Case的操作來觀察是否有異常現象;不過這種替代方式的操作數比不上Robot而會有某些狀況無法測出的缺點。
2008年5月21日 星期三
P14 測試的過程(5)──進行系統設定測試與效能測試
由下而上一層層針對操作功能的測試告一段落後,接下來要做的是系統環境與設定的改變測試。對於一些不提供給使用者變更的執行參數,一般會放在設定檔案或是資料庫裡,測試時就逐一改變存放於外部的設定值並觀察是否依設計影響系統的行為。
這時候的設定vs Package追溯表就顯得很重要:改了什麼設定會影響哪些Package,Package又在哪些Use Case裡被使用,根據這個表格才能確認是否一切都正常。通常系統設定測試的內容會在Test Case時一併考慮,以節省再度重覆測試Test Case的時間。
與功能的流程、動作有關的測試結束後,還會進行非功能的測試,其中最重要的是效能測試。客戶對於一些重要的Use Case(例如Client送到Server再回來的反應時間,使用者按下某鍵後的反應時間,或是Client Server間傳送的流量等量化的測量),都會要求在特定條件下耗費的時間上限。
對於執行時間常見的測試法是在一進入指定的方法就先記下開始時間,執行返回前最後一步再取一次現在時間並減去之前記下的開始時間,所得即是執行總共所花的時間。我大多測試七次後取中間的五次作平均並記錄下來。測試流量則是在負責收送的架構元件上記下傳送與接收字串的長度。如果有不合規定規定的現象則要研判原因並加以修正。
這時候的設定vs Package追溯表就顯得很重要:改了什麼設定會影響哪些Package,Package又在哪些Use Case裡被使用,根據這個表格才能確認是否一切都正常。通常系統設定測試的內容會在Test Case時一併考慮,以節省再度重覆測試Test Case的時間。
與功能的流程、動作有關的測試結束後,還會進行非功能的測試,其中最重要的是效能測試。客戶對於一些重要的Use Case(例如Client送到Server再回來的反應時間,使用者按下某鍵後的反應時間,或是Client Server間傳送的流量等量化的測量),都會要求在特定條件下耗費的時間上限。
對於執行時間常見的測試法是在一進入指定的方法就先記下開始時間,執行返回前最後一步再取一次現在時間並減去之前記下的開始時間,所得即是執行總共所花的時間。我大多測試七次後取中間的五次作平均並記錄下來。測試流量則是在負責收送的架構元件上記下傳送與接收字串的長度。如果有不合規定規定的現象則要研判原因並加以修正。
2008年5月20日 星期二
P13 產出設計文件(5)──記錄Use Case與Module的關聯
等到使用者測試快到結束時,流程上的程式大致也趨近穩定,這個時候就可以開始記錄Use Case與Module之間的使用關聯。這裡的作法與Component Flow、Component Action幾乎雷同,只是層次換到Use Case Flow、Use Case Activity(對應到Module Interface Method)來記錄。
在直覺的設計下,如果目標放在如何完成功能的話,除了會造成處理流程只有達到功能的分解動作而失去層次感,這樣一來作任何修改都面臨無法縮小影響範圍的風險,因為執行順序的分工或是執行動作的深度都涵蓋了所有的層次,使用從上到下都有可能被影響。造船明明只造一個船體並依需要分割艙房即可使用,為什麼要多造出數層看似無用的防水匣門呢?這當然是為了預防“萬一”船體漏水時能夠限制影響的範圍。
系統開發已近尾聲,其實已經可以發現設計的方向可以導成遞迴處理,只要我們的產出是有規律且有層次的,就可以切割為處理方式相同的多個層次。另一方面,設計者在設計時只需要將注意力放在切割後的空間,可以在這限制的範圍內儘情陳設需要的結構而不用擔心影響其他地方;設計的產出遵照設計準則的話,還可以使用一些小工具來產生必要的資訊與文件。
設計的時候多花一些時間安排結構,可以回收的是快速找出問題點、修改時可限制影響範圍、容易讓別人瞭解並使用、減少文件的製作時間以及穩定地重用並擴充。設計時損失的時間可以在未來數倍甚至數十倍地回收回來,這還不足以令人轉變開發系統的觀念嗎?
在直覺的設計下,如果目標放在如何完成功能的話,除了會造成處理流程只有達到功能的分解動作而失去層次感,這樣一來作任何修改都面臨無法縮小影響範圍的風險,因為執行順序的分工或是執行動作的深度都涵蓋了所有的層次,使用從上到下都有可能被影響。造船明明只造一個船體並依需要分割艙房即可使用,為什麼要多造出數層看似無用的防水匣門呢?這當然是為了預防“萬一”船體漏水時能夠限制影響的範圍。
系統開發已近尾聲,其實已經可以發現設計的方向可以導成遞迴處理,只要我們的產出是有規律且有層次的,就可以切割為處理方式相同的多個層次。另一方面,設計者在設計時只需要將注意力放在切割後的空間,可以在這限制的範圍內儘情陳設需要的結構而不用擔心影響其他地方;設計的產出遵照設計準則的話,還可以使用一些小工具來產生必要的資訊與文件。
設計的時候多花一些時間安排結構,可以回收的是快速找出問題點、修改時可限制影響範圍、容易讓別人瞭解並使用、減少文件的製作時間以及穩定地重用並擴充。設計時損失的時間可以在未來數倍甚至數十倍地回收回來,這還不足以令人轉變開發系統的觀念嗎?
2008年5月19日 星期一
P12 發現動作組合後的陷阱
在很多時候,只看單一動作的實現步驟時感覺不到任何異常而認為合理,但不知為什麼只要把幾個動作組合為功能實作後,就會發生意想不到的問題。整合測試與使用者測試除了要確認完成功能所需的動作是否依序執行之外,同時也要找出動作組合後才看得到的潛在問題。
UI是最容易產生陷阱的地方。例如focus的改變可以即時呼叫requestFocus(),但是在呼叫在內部會以另一個Thread來進行動作,這就會產生時間差的問題;有時誤以為呼叫後會立即生效,就接著在後面寫上預期focus已經移動的程式,問題有時就這麼出現。另一個可能是有兩個動作都有對不同UI元件requestFocus()的動作,這在各自檢視動作時並無不妥,可是兩個動作在同一個流程內一起經過時,就會發現focus有時會落在另一個UI元件上。
在流程內應該只執行一次的動作(像requestFocus())被呼叫過兩次就有可能產生問題,從流程上來看應該是經過所有動作的同時判斷該不該作那個動作。因此含有那個動作的方法可能會增加“要不要執行那個動作”的參數,或者是把那個動作另外拉出成另一個方法在流程裡自行呼叫,一切端看怎麼做最合邏輯。
在這個階段還有可能作方法的重構,是一定要具備的心理準備,如果動作不能迅速而安全地搬移或拆解,就會在此時付出嚴重的side effect代價。
註:有時在組合動作裡難以決定要怎麼做,那時會在被重覆使用的地方加上synchronized以保證一次只有一個thread進入。即使做兩次是浪費資源,但是至少每一次的結果都是正確的。
UI是最容易產生陷阱的地方。例如focus的改變可以即時呼叫requestFocus(),但是在呼叫在內部會以另一個Thread來進行動作,這就會產生時間差的問題;有時誤以為呼叫後會立即生效,就接著在後面寫上預期focus已經移動的程式,問題有時就這麼出現。另一個可能是有兩個動作都有對不同UI元件requestFocus()的動作,這在各自檢視動作時並無不妥,可是兩個動作在同一個流程內一起經過時,就會發現focus有時會落在另一個UI元件上。
在流程內應該只執行一次的動作(像requestFocus())被呼叫過兩次就有可能產生問題,從流程上來看應該是經過所有動作的同時判斷該不該作那個動作。因此含有那個動作的方法可能會增加“要不要執行那個動作”的參數,或者是把那個動作另外拉出成另一個方法在流程裡自行呼叫,一切端看怎麼做最合邏輯。
在這個階段還有可能作方法的重構,是一定要具備的心理準備,如果動作不能迅速而安全地搬移或拆解,就會在此時付出嚴重的side effect代價。
註:有時在組合動作裡難以決定要怎麼做,那時會在被重覆使用的地方加上synchronized以保證一次只有一個thread進入。即使做兩次是浪費資源,但是至少每一次的結果都是正確的。
2008年5月18日 星期日
P11 測試的過程(4)──讓使用者確認系統沒有問題
在經過一連串的內部測試,確定在較正常的操作下可以無誤地執行功能後就準備進入使用者測試階段。在此之前要保證輸入後的執行與傳回都必須可以正常完成,因為使用者測試主要是測試各種輸入的狀況並確認執行時的檢查與傳回之顯示是否正確。
進行的方式與Test Case大致相同,不過使用者會根據他們的工作習慣來操作,同時還會故意用錯誤的操作與不正確的值來檢驗系統是否能夠防止並提示。功能的執行大多是線性的,頂多會有流程的分歧點指向另一段線性流程,但是UI介面的設計就具有很高的複雜度,這是因為使用者的每一個輸入動作都是獨立的功能,許多UI的動作又會相互牽動造成影響。我們可以想見在使用者測試時會冒出許多大大小小的錯誤回報。
使用者會根據交易的生命週期來切割出每一個交易應測試的範圍,然後就從開啟畫面、輸入資料、事件觸發、資料檢查、訊息提示、執行功能、畫面切換、結果回報……等等不同的層面來檢查系統各個階段的正確與錯誤的處理方式。對於使用者回報的問題,我們必須具有快速分辨出問題屬於哪個Module或Component問題的能力,方能快速、準確地定位問題並加以解決;如果一時之間無法找出發生點,可以經由討論與測試來縮小範圍以便定位。
解決使用者測試出來的問題,最忌諱的是產生side effect與defect reopen,這會令使用者感覺系統極端不穩定。問題應該越解越少,問題數能夠持續收斂的系統是可以預期在哪一天可以結束測試的。
進行的方式與Test Case大致相同,不過使用者會根據他們的工作習慣來操作,同時還會故意用錯誤的操作與不正確的值來檢驗系統是否能夠防止並提示。功能的執行大多是線性的,頂多會有流程的分歧點指向另一段線性流程,但是UI介面的設計就具有很高的複雜度,這是因為使用者的每一個輸入動作都是獨立的功能,許多UI的動作又會相互牽動造成影響。我們可以想見在使用者測試時會冒出許多大大小小的錯誤回報。
使用者會根據交易的生命週期來切割出每一個交易應測試的範圍,然後就從開啟畫面、輸入資料、事件觸發、資料檢查、訊息提示、執行功能、畫面切換、結果回報……等等不同的層面來檢查系統各個階段的正確與錯誤的處理方式。對於使用者回報的問題,我們必須具有快速分辨出問題屬於哪個Module或Component問題的能力,方能快速、準確地定位問題並加以解決;如果一時之間無法找出發生點,可以經由討論與測試來縮小範圍以便定位。
解決使用者測試出來的問題,最忌諱的是產生side effect與defect reopen,這會令使用者感覺系統極端不穩定。問題應該越解越少,問題數能夠持續收斂的系統是可以預期在哪一天可以結束測試的。
2008年5月17日 星期六
P10 測試的過程(3)──驗證Use Case Scenario的整合測試
Test Case的目的是要驗證Use Case的正確性。Use Case在其所屬的Activity Diagram裡已經被切分為Flow與Activity,而Activity已經於功能測試裡保證其動作合乎需要,因此Test Case的重心將會是在Use Case Flow控制的正確性。
測試的想法與功能測試、Use Case是相同的,先分析出Use Case流程裡執行路徑的最大數,再決定在分歧點要準備的資料是什麼,而那些資料必須執行哪些動作才會出現,然後就依序將那些動作寫成測試步驟;記得每一種測試方法就是一個Test Case(換句話說,一個Use Case有一個或一個以上的Test Case)。
由於Use Case Senarios的操作大多經由UI的動作才能備齊,所以這部分的測試都會由客戶的測試人員進行。使用者在畫面上輸入劇本裡的資料後執行,同時檢視系統反應出來的結果是否合乎預期。如果整合測試時已經測試過純資料的執行,那麼此時只需要注重使用者輸入資料的正確與否、執行UI資料的收集與執行後反應在UI上的動作即可。
使用者的操作會出現許多瑣碎的輸出入反應,這些也是會被回報為錯誤;不過同時也會發現實際操作的使用者測試出來的問題,有很多都是在需求、設計與測試時沒有顧慮到的。為了應付問題修正時的改變,保持彈性的設計真的是必要的。
2008年5月16日 星期五
P09 產出設計文件(4)──記錄Package之間的關聯
在前面的Package Diagram與Class Diagram,我都會先作出“擁有關聯”的靜態圖表,先檢視結構編制。使用的關聯一般不會另外作出圖表,因為每一個Package或Class都會使用許多其他物件,如果要將所有的關聯物件拉到同一張圖裡,一定會發生過於擁擠且根本看不清楚的狀況。
另一個困擾是在程式裡面的寫法並無法在使用Rose倒回程式碼時顯示出來,因此會需要再花人力去更新為真實情況。對於這種需要再額外花資源的更新我一向比較排斥,所以在查詢使用關聯的時候,都會使用Reference功能尋找以及檢視import內容來達成。
要記得關聯是在流程或動作時使用其他物件來完成功能所產生的,只要流程或動作有所變化它就會跟著改變。由於使用的關聯錯踪複雜,並不希望把使用關聯同時顯示在一張圖裡;這時的希望仍然寄望在Java構文解析,指定某個Package、Class或是Method後就自動列出我們想要的資訊。
在CMMI裡一般都會要求水平追溯表與垂直追溯表,在非做不可的限制下就以Excel勾選Package vs Package、Package vs Component這兩個追溯表,至於Package vs Class可以參照Eclipse Package Explorer取得,而Class vs Class就直接看程式碼判別吧。
另一個困擾是在程式裡面的寫法並無法在使用Rose倒回程式碼時顯示出來,因此會需要再花人力去更新為真實情況。對於這種需要再額外花資源的更新我一向比較排斥,所以在查詢使用關聯的時候,都會使用Reference功能尋找以及檢視import內容來達成。
要記得關聯是在流程或動作時使用其他物件來完成功能所產生的,只要流程或動作有所變化它就會跟著改變。由於使用的關聯錯踪複雜,並不希望把使用關聯同時顯示在一張圖裡;這時的希望仍然寄望在Java構文解析,指定某個Package、Class或是Method後就自動列出我們想要的資訊。
在CMMI裡一般都會要求水平追溯表與垂直追溯表,在非做不可的限制下就以Excel勾選Package vs Package、Package vs Component這兩個追溯表,至於Package vs Class可以參照Eclipse Package Explorer取得,而Class vs Class就直接看程式碼判別吧。
2008年5月15日 星期四
P08 產出設計文件(3)──用適當的方式補完動態圖表
UML的支持者可能會推薦製作Sequence Diagram或Collaboration Diagram,但是就如我所提過它們只能提供線性的執行順序而不易表達出分歧流程的現象,所以我幾乎不製作這兩種圖;當然,如果公司堅持要產出這種圖,也只好咬著牙畫出來交差。
利用Component結構的分工,流程與動作都分別在放在Flow與Action裡,我也建議在每一個分解的動作使用註解來表達該段程式的意義。因此只要順著Component Interface Method進入到Flow,幾乎就可以掌握到達成功能的必要經過,所以我沒有要求過一起開發的人畫過任何一張Sequence Diagram或Collaboration Diagram。這一方面因為Flow就足以表達所要傳達的意義,另一方面也是因為讓分析時的Activity Diagram等同於設計的流程與動作。
在所有Component都具有相同結構時,以Java構文解析技術去拆解Flow的程式碼搭配註解,再呼叫製作流程圖工具的API來產出流程圖,等到這個程度時甚至連Activity Diagram都可以省略,直接在Component裡設計。這必須要具有相當經驗後才能這麼做以免一做下去就亂成一團,另外可惜的是Rose並沒有提供這種API。
製作文件的時間點我認為在Module作完功能測試或Component作完Unit Test後就製作它的圖表,一方面是因為測試後比較穩定而較少修改,另一方面則是趁記憶猶新時加以記錄。(以Package為單位)
利用Component結構的分工,流程與動作都分別在放在Flow與Action裡,我也建議在每一個分解的動作使用註解來表達該段程式的意義。因此只要順著Component Interface Method進入到Flow,幾乎就可以掌握到達成功能的必要經過,所以我沒有要求過一起開發的人畫過任何一張Sequence Diagram或Collaboration Diagram。這一方面因為Flow就足以表達所要傳達的意義,另一方面也是因為讓分析時的Activity Diagram等同於設計的流程與動作。
在所有Component都具有相同結構時,以Java構文解析技術去拆解Flow的程式碼搭配註解,再呼叫製作流程圖工具的API來產出流程圖,等到這個程度時甚至連Activity Diagram都可以省略,直接在Component裡設計。這必須要具有相當經驗後才能這麼做以免一做下去就亂成一團,另外可惜的是Rose並沒有提供這種API。
製作文件的時間點我認為在Module作完功能測試或Component作完Unit Test後就製作它的圖表,一方面是因為測試後比較穩定而較少修改,另一方面則是趁記憶猶新時加以記錄。(以Package為單位)
2008年5月14日 星期三
P07 產出設計文件(2)──倒回Rose補完靜態圖表
實作進行到一定程度,系統內的Class一多起來,Workspace拉開後的Package、Class清單排成一大串,根本不知道其間的從屬關係。一堆被壓平為階層式排法的檔案並無法詳細表達三度空間的關聯,因此繪製靜態的UML圖表交待關聯是勢在必行的。
在最上層需要Package Diagram來表示Module間的從層關係,即使同樣是Module也要依使用與被使用的層次由上而下排下來,同一層的儘量水平地方放在一排,接著還需要一組Package Diagram來表示Module與Component間的使用關係。如此一來我們就可以在這兩張圖裡立即看出Module的層次與使用Component的關係。當然要記得要同時放上Modele Interface與Component Interface以顯示提供Method的資訊。
接著以每一個Package為單位,繪製各自的Class Diagram。其實在所有Package內部結構都相同的情況下,各別拉出的Class Diagram幾乎都一樣,差別只在於Component Interface、Implementation、Flow、Action、Properties、Model與Exception各自提供了哪些Method,未來在修改或新增Controller那一層時就立即可以知道Package裡提供了哪些功能。
當然,這些在Rose下可以直接由整個Workspace倒回Rose Model,只需要找人依實際使用狀況與Component結構拉好就可以保存;日後即使在方法上有重構的現象,由於Method移動的範疇已在軟體架構上提供,所以圖都不用調整只要再從程式碼直接倒回即可更新。
在最上層需要Package Diagram來表示Module間的從層關係,即使同樣是Module也要依使用與被使用的層次由上而下排下來,同一層的儘量水平地方放在一排,接著還需要一組Package Diagram來表示Module與Component間的使用關係。如此一來我們就可以在這兩張圖裡立即看出Module的層次與使用Component的關係。當然要記得要同時放上Modele Interface與Component Interface以顯示提供Method的資訊。
接著以每一個Package為單位,繪製各自的Class Diagram。其實在所有Package內部結構都相同的情況下,各別拉出的Class Diagram幾乎都一樣,差別只在於Component Interface、Implementation、Flow、Action、Properties、Model與Exception各自提供了哪些Method,未來在修改或新增Controller那一層時就立即可以知道Package裡提供了哪些功能。
當然,這些在Rose下可以直接由整個Workspace倒回Rose Model,只需要找人依實際使用狀況與Component結構拉好就可以保存;日後即使在方法上有重構的現象,由於Method移動的範疇已在軟體架構上提供,所以圖都不用調整只要再從程式碼直接倒回即可更新。
2008年5月13日 星期二
P06 產出設計文件(1)──邊寫測試內容邊補程式說明
在撰寫Unit Test或是功能測試文件的時候,雖然理論上只要根據Interface Method的描述就可以寫出來,但是為了測試的完整性還是仔細參考Method內部的流程與資料比較好。
理論上測試程式應該由設計的人撰寫而且不要讓實作的人知道,以便在不知測試內容的狀況下真正測出程式的正確性,但是在實際的專案裡大多得自己進行Unit Test 甚至是功能測試,得到整合測試時才會有其他的人幫忙。在人手不足的專案裡,如果必須在自己實作後撰寫測試程式的內容時,倒是個review與補足註解的良好時機。
完整的Unit Test應參考所有的流程分歧點與動作的參數。在準備Unit Test資料的時候可以先檢查待測試的流程種類並決定如何來進入不同的分歧流程;進行動作的檢查時再參考執行時需傳入的資料作最大化的測試。在檢視的同時一邊調整、一邊補上註解與Data、Method的Java Doc,如此一來就能夠兼顧Unit Test、Review、Refactoring與Comment、Documentation。
理論上測試程式應該由設計的人撰寫而且不要讓實作的人知道,以便在不知測試內容的狀況下真正測出程式的正確性,但是在實際的專案裡大多得自己進行Unit Test 甚至是功能測試,得到整合測試時才會有其他的人幫忙。在人手不足的專案裡,如果必須在自己實作後撰寫測試程式的內容時,倒是個review與補足註解的良好時機。
完整的Unit Test應參考所有的流程分歧點與動作的參數。在準備Unit Test資料的時候可以先檢查待測試的流程種類並決定如何來進入不同的分歧流程;進行動作的檢查時再參考執行時需傳入的資料作最大化的測試。在檢視的同時一邊調整、一邊補上註解與Data、Method的Java Doc,如此一來就能夠兼顧Unit Test、Review、Refactoring與Comment、Documentation。
2008年5月12日 星期一
P05 測試的過程(2)──Module Interface與Activity Interface的功能測試
底層的Component的出版理論上都已經通過自己的Unit Test,因此在專案裡選用這些Component都可以先不用擔心會有錯誤,能夠將注意力放在與Use Case實現有關的Activity Interface與完成Activity功能的Module Interface。
測試的範圍同樣以Interface Method為對象,測試的方式與原則與上一篇的內容相同,不同的是Module與Activity都屬於專案的範圍,應由專案內安排人力負責這個測試。功能測試的目的是測試各個分解動作是否都依照設計的要求運作,當我們確認所有的動作都正確地被實作出來後,接下來就能將眼光放到更上一層的整合測試。
其實功能測試的作法與Component Unit Test幾乎相同,不過測試的對象都屬於專案上負責功能的物件,而且測試時通常需要建立較複雜的環境。Unit Test測試的是底層元件的功能,功能測試則是測試專案元件的功能,在設計時記得儘量讓專案元件封裝底層元件;原因是專案元件提供的功能會符合專案Domain的意義,而底層元件只是達成特定計算機行為之瑣碎功能,並不適合專案上直接使用。
測試的範圍同樣以Interface Method為對象,測試的方式與原則與上一篇的內容相同,不同的是Module與Activity都屬於專案的範圍,應由專案內安排人力負責這個測試。功能測試的目的是測試各個分解動作是否都依照設計的要求運作,當我們確認所有的動作都正確地被實作出來後,接下來就能將眼光放到更上一層的整合測試。
其實功能測試的作法與Component Unit Test幾乎相同,不過測試的對象都屬於專案上負責功能的物件,而且測試時通常需要建立較複雜的環境。Unit Test測試的是底層元件的功能,功能測試則是測試專案元件的功能,在設計時記得儘量讓專案元件封裝底層元件;原因是專案元件提供的功能會符合專案Domain的意義,而底層元件只是達成特定計算機行為之瑣碎功能,並不適合專案上直接使用。
2008年5月11日 星期日
P04 輔助的文件(6)──從Excel文件產出Unit Test空殼
在開發元件的同時,可以從Excel文件裡根據Class Name與Method Name來產生Unit Test的程式空殼,這樣可以省掉一點從每個Class逐一產生Unit Test Class並勾選待測試方法的單調工作。
以Package為單位,每個要測試的Class應該要產生一個對應的UT Class放在相同名稱的Package;UT Class內應有的UT Method則以Package Interface為基準先行建立。在每一個Package的結構都相同、Constructor也相同的情形下,setUpBeforeClass()與setUp()兩個預備方法都可以預先建立預設型式加以修改,甚至tearDownAfterClass()就固定呼叫disposeObject()即可。
產生空殼並準備好測試前的程式碼後,就依照上一篇的原則撰寫測試程式碼進行Unit Test。每一個Class測試完後,記得依每個Project擁有一個Test Suite的原則,把該UT Class加入到Test Suite裡,未來可以用Project為單位,一次測試整個Project的所有Class的所有Method。
對我而言,未來還會在Interface Method後面定義各種不同的Method測試參數種類,在產生測試程式空殼時順便產生各種參數的呼叫程式碼,如此一來只要再補上Assert的內容就可以再加快Unit Test的準備速度。
以Package為單位,每個要測試的Class應該要產生一個對應的UT Class放在相同名稱的Package;UT Class內應有的UT Method則以Package Interface為基準先行建立。在每一個Package的結構都相同、Constructor也相同的情形下,setUpBeforeClass()與setUp()兩個預備方法都可以預先建立預設型式加以修改,甚至tearDownAfterClass()就固定呼叫disposeObject()即可。
產生空殼並準備好測試前的程式碼後,就依照上一篇的原則撰寫測試程式碼進行Unit Test。每一個Class測試完後,記得依每個Project擁有一個Test Suite的原則,把該UT Class加入到Test Suite裡,未來可以用Project為單位,一次測試整個Project的所有Class的所有Method。
對我而言,未來還會在Interface Method後面定義各種不同的Method測試參數種類,在產生測試程式空殼時順便產生各種參數的呼叫程式碼,如此一來只要再補上Assert的內容就可以再加快Unit Test的準備速度。
2008年5月10日 星期六
P03 測試的過程(1)──對所有Component Interface作Unit Test
Component與API可以說是基本的積木,所有的系統主要都是先取用元件庫裡的元件根據客戶需要慢慢堆疊出來的;正因為會有很多系統都會採用基本的元件來使用,身負各個系統“絕對重用”責任的元件更提供有正確的功能,才能夠讓各個系統無後顧之憂地專注在開發上,以免因下層的改動而導致上層的混亂。
正因元件庫是系統的根本,所以元件與API的每一個動作都應該保證正確,才能夠再測試上一層的流程。在Unit Test的時候,主要是在確保Component Interface Method或API方法的功能是正確的,此時可以不用測試到裡面的每個Interface以免累死;不過當實作的Class有數個時,每一種實作Class都必須建立起來然後完整地測試一遍。
完整的測試,是指方法內的流程所有的支線都涵蓋在測試的範圍裡,而且每種狀況的所有輸入資料組合都有測試。首先計算流程所有的分支點,再列出改變執行流程的所有集合,接著再根據每一段流程片段所應處理與不應處理的資料加以記錄,列表為測試的內容建立Unit Test的內容;每個測試方法都依此方式建立,就能夠建立趨近完整的Unit Test。(撰寫完整的Unit Test所需的時間會比定義及開發功能還要多)
如果開發後的Component或API方法抽來換去的,原先為這個方法寫的Unit Test幾乎得全部作廢重寫,相信沒有人會這樣做的,因此定義好Component與API的功能方法並維持定義的不變,是建立方便重用元件庫的基本要素。
正因元件庫是系統的根本,所以元件與API的每一個動作都應該保證正確,才能夠再測試上一層的流程。在Unit Test的時候,主要是在確保Component Interface Method或API方法的功能是正確的,此時可以不用測試到裡面的每個Interface以免累死;不過當實作的Class有數個時,每一種實作Class都必須建立起來然後完整地測試一遍。
完整的測試,是指方法內的流程所有的支線都涵蓋在測試的範圍裡,而且每種狀況的所有輸入資料組合都有測試。首先計算流程所有的分支點,再列出改變執行流程的所有集合,接著再根據每一段流程片段所應處理與不應處理的資料加以記錄,列表為測試的內容建立Unit Test的內容;每個測試方法都依此方式建立,就能夠建立趨近完整的Unit Test。(撰寫完整的Unit Test所需的時間會比定義及開發功能還要多)
如果開發後的Component或API方法抽來換去的,原先為這個方法寫的Unit Test幾乎得全部作廢重寫,相信沒有人會這樣做的,因此定義好Component與API的功能方法並維持定義的不變,是建立方便重用元件庫的基本要素。
2008年5月9日 星期五
P02 所有修改都要為變動範圍註明原因
修改程式的原因如果不是為了重構就是為了除錯,無論如何都會有一個原因驅使我們去變動。在修改的同時,會希望把變動的原因以註解的方式記錄在程式碼裡,這樣就不會讓這次的改變在未來被淡忘。
註解除了記錄單次的改變之外,另一個目的是減少維護時的痛苦。對於後來接手程式的人來說,最好的狀況是只看現在版本的程式就能夠獲得所有版本的變動資訊,否則面對什麼註解都沒有的程式,勢必得拉出所有版本的內容才能明白哪裡有修改,基本上拿到一個版本號碼超過十的程式就不會有人再拉出來比對。
從這個角度來看可以明白程式裡適當的註解有多麼重要,可以讓所有人避免去比對多個檔案才能找出差異,而讓差異在同一份文件就看得出來,而且知道每一個差異發生的原因,這會是讓其他人迅速瞭解內部作法的捷徑。
註解除了記錄單次的改變之外,另一個目的是減少維護時的痛苦。對於後來接手程式的人來說,最好的狀況是只看現在版本的程式就能夠獲得所有版本的變動資訊,否則面對什麼註解都沒有的程式,勢必得拉出所有版本的內容才能明白哪裡有修改,基本上拿到一個版本號碼超過十的程式就不會有人再拉出來比對。
從這個角度來看可以明白程式裡適當的註解有多麼重要,可以讓所有人避免去比對多個檔案才能找出差異,而讓差異在同一份文件就看得出來,而且知道每一個差異發生的原因,這會是讓其他人迅速瞭解內部作法的捷徑。
2008年5月8日 星期四
P01 儘早來幾個Scenario Prototype
設計時幾乎只是純粹的思考,就像事先擬定工作計畫一樣,決定好的工作順序有時得開始實行後才知道執行順序是否正確以及動作是否需要增減。設計上雖然可以經由Review來修正看得到的問題,但是人為的動作都可能有盲點存在,一群人一致通過的設計並不代表一定可行。
因此在設計的初期,決定好架構與Module後,會針對系統的啟動與幾個重要的執行功能先行試作幾個Scenario Prototype,藉此找出在設計時被忽略掉的動作。在2007/11的專案裡,架構鋪設好後,就為了要不要先作一大堆的需求與設計文件頭痛,最後決定面對現實,先確定幾個重要功能再說。
在製作Scenario Prototype時,重點在於檢查流程的處理過程,至於動作的實作可以直接傳回要用的值來處理,而不要堅持連動作都要做好才測試以便加快速度。在測試的過程中一方面檢視自己的設計,一方面測試是否都執行到應該要做的動作,先確定系統重要的骨幹可以正常執行後再往下設計會是較安全的作法。
因此在設計的初期,決定好架構與Module後,會針對系統的啟動與幾個重要的執行功能先行試作幾個Scenario Prototype,藉此找出在設計時被忽略掉的動作。在2007/11的專案裡,架構鋪設好後,就為了要不要先作一大堆的需求與設計文件頭痛,最後決定面對現實,先確定幾個重要功能再說。
在製作Scenario Prototype時,重點在於檢查流程的處理過程,至於動作的實作可以直接傳回要用的值來處理,而不要堅持連動作都要做好才測試以便加快速度。在測試的過程中一方面檢視自己的設計,一方面測試是否都執行到應該要做的動作,先確定系統重要的骨幹可以正常執行後再往下設計會是較安全的作法。
訂閱:
文章 (Atom)