如果讀者對上面我的分析過程能夠正確地理解,相信對于這份代碼的bug也是不難找到的:問題出在條件判斷上面。
這個條件判斷貌似少點什么!沒錯,分支只判斷c是否等于k,而根本沒有考慮(k-c)的奇偶問題;換句話說,代碼少考慮情況了!那么Challenge就變得很容易了,只要把代碼中缺少的部分提煉出來即可,例如示例中的第三組測試數據originalWord="aaa", finalWord="bab",k=4,按照這份代碼的結果會是"IMPOSSIBLE",而實際上(k-c)是偶數,應該返回"POSSIBLE",這樣我們的Challenge又成功了!不過這次與上一次的Challenge是有所不同的,因為這份代碼并非所有測試用例都通過不了,例如示例中的第二個測試用例,當originalWord與finalWord相同的時候,代碼是可以通過的。因此我們稱這種僅能通過部分測試用例的情況為對需求的片面分析或需求項分析不足。
我們的Challenge過程很好地闡述了如何針對這種情況設計測試用例:將需求項分析不足的地方提取出來,專門設計這方面的用例。當然,真實應用中,我們會設計大量測試用例,但需求項的覆蓋問題依然是重中之重,因為如果測試人員在解讀需求的時候不能將其完全覆蓋,設計的測試用例必然有所遺漏,可能會造成測試失敗。所以,測試用例對需求的完全覆蓋,即恰當地進行邏輯測試是測試人員必須加以重視的內容。
正確的代碼總是相同的,而錯誤的代碼則各有各的問題,我們再來看另一份代碼:
判斷部分似乎真的沒什么問題,只是形式和我的例程有所差異,那這份代碼是怎么被成功Challenge的呢?細心的讀者可能一眼就能發現問題所在,前面的循環有問題!結束條件居然是i
顯然這段代碼不能完全實現題目中的需求,我們只需設計一個測試用例使得最后一個字符左右了程序的輸出結果即可。例如,originalWord="aaa", finalWord="aab",k=0,如果沒有最后一個字符,顯然應該返回"POSSIBLE",然而最后一個字符的存在使得結果變成了"IMPOSSIBLE",而這一切對于上面的代碼而言就好像在比較originalWord= finalWord ="aa"一樣,所以該測試用例成功Challenge了該代碼!
對于這份代碼的問題,我們通常叫做邊界問題處理不當。邊界可能的含義有很多,例如輸入變量的范圍,例如系統的吞吐量極限等等,在這個問題中,邊界的意義就是循環的初始和結束條件。很顯然,被我們成功Challenge 的代碼初始條件做的沒有問題,但結束條件卻是錯的,因為最后一個字符沒有被處理!而我們設計測試用例的時候便順水推舟,將最后一個字符起決定作用的測試用例設計出來,軟件的bug就暴露了!所以任何測試一定不能放過邊界問題的處理,對邊界條件設置測試用例勢在必行。
看來Challenge不是一件多難的事,那么來看下面一位選手的代碼吧:
相信一看到這份代碼,大家會十分吃驚,居然代碼只有兩行,這能實現要求的功能嗎?話說回來,我當初看到這份代碼的時候也覺得看來這兩行兒戲般的代碼兇多吉少了,于是將之作為重點Challenge對象。不過細細讀來,你和我都會發現,沒那么簡單,這是因為這份代碼對需求的實現沒有問題,囊括了所有需求項,邊界處理也很得當,任何用例都不可能Challenge成功,這份代碼是正確的。因此,只要我們的測試遵循原則,尊重需求,沒有遺漏,考慮邊界,不僅能夠找到軟件的bug,也可以證明軟件實現需求的正確性。
說了這么多,相信讀者對設計有效的測試用例應該有了一定的了解,不過真實系統中遇到的問題遠比算法設計題目要復雜得多,設計的測試用例也要涵蓋更多內容,例如除了功能測試、邏輯測試和邊界測試外,還可能有余量測試、接口測試、強度測試、容錯性測試等。不過,萬變不離其宗,任何測試都是基于對需求的準確、完整的分析;而測試人員在測試過程中也應該堅持心思縝密的作風,這樣軟件bug將無處遁形,被我們一網打盡!
原文轉自:http://www.uml.org.cn/Test/201212274.asp