測試先行,指的是在撰寫實際的程式碼之前,先撰寫好測試這段程式碼的程式碼。

這樣的作法對於沒有接觸過這個概念的人來說,或許有點超乎常理。可以觀察到的現況是,並不是每位程式設計者都會為每段自己所實作的程式碼,撰寫對應的單元測試程式,更別說,希望程式設計者在撰寫程式之前,先把測試程式撰寫好了。

在前文中有提到,先撰寫測試程式的好處之一,便是協助程式設計者轉換觀點。從設計者轉換成為測試者的觀點,有助於在著手設計時,全盤考慮到各種可能會遭遇的情境,進而使程式設計的更為完備。而從設計者切換到使用者的觀點,可以實際體驗客戶端程式設計者的感受及體驗,有助於自此回饋改善自身的設計。

當然,準備好足夠完備的測試程式碼,可以為程式碼快速、頻繁的變動,提供足夠的信心,也是撰寫單元測試程式的關鍵好處。

先撰寫測試程式會有怎樣的後果?
既然有這麼多的好處,為什麼我們沒有看到眾多的程式設計者都採用這種「測試先行」的方法呢?這其中的關鍵在於撰寫測試程式碼並非毫無代價,而且事實上,必須付出的代價看起來還不輕,而這正是許多程式設計者不願意嘗試這種方法的原因。

究竟需要撰寫多少測試程式碼呢?根據許多人的實驗經驗,需要撰寫的測試程式碼通常和實作的程式碼數量相當,也就是說通常會是1:1,有時候,甚至會到3:2的比例。很多程式設計者每天辛勤的工作,只求希望能在期限之前完成實作的程式碼。現在,要他們除了原來就要實作的程式碼以外,還得多實作份量一樣重、甚至更重的程式碼。而且,更重要的是,還沒開始撰寫實際上要用到的程式碼之前,就得先把這些測試程式碼給完成。這對於很多人來說,在心理的接受度上,會是一個很大的考驗。即使先撰寫測試程式碼,的確可以帶來不少好處,但是,為了這些好處,必須增加所撰寫程式碼的份量,是否便會拉長撰寫程式碼所需的時間,進而影響到時程呢?為了這些好處,來影響開發的時程,這對於總是在與期限奮鬥的程式設計者來說,絕對是會構成很大的心理障礙的。

不過如果深入的思考,你或許會發現,先撰寫測試程式碼,的確有可能得到相同的開發時間,甚至是讓開發時間縮短。所需的時間當然取決於許多因素,但這件事的確有所可能。

必寫測試程式碼、而且先寫測試程式碼,把撰寫程式碼的份量加倍,結果還能縮短開發時間,這件事就和搭檔編程(Pair Programming)是類似的情況。讓兩個人坐在一起寫同一個程式,還認為這樣子生產力會比較好,怎麼說都十分違背一般人所認知的常理。

當然,會採用這種超乎常理的開發方法,雖然有時候光倚靠對大師們的景仰或對教條的信仰,就有可能辦得到。不過,這背後應該還是存在一些理性的思考,足以讓人相信,採取先撰寫測試程式的方式,的確不會增加開發時間,甚至還可以縮短開發時間。

寫完程式碼只是開發過程中的一部分
有些人對於開發時間一直有個難以打破的迷思,不僅僅是程式設計者會有這樣的問題,就連非程式設計者也往往這麼想,是什麼迷思呢?就是認為程式碼寫完了,開發就結束或是差不多結束了。這樣的習慣,也使得不少人往往忽略其他階段,尤其是測試階段所耗費的時間。所以,開發時間不等同或近似於撰寫程式碼的時間,撰寫程式碼的時間,其實只有占一部份

先撰寫測試程式碼,有可能在那些環節上來縮短開發時間呢?首先,是因為設計簡化所帶來的程式碼簡化。

因為撰寫測試程式碼時,等於是從客戶端程式設計者的角度來撰寫程式。客戶端程式設計者不會喜歡面對複雜的介面,因此,當你同時扮演客戶端程式設計者時,很容易地,便會試著將介面設計調整成為盡可能簡化的情況,因為只有這樣子,客戶端程式設計者才容易撰寫程式。簡化過後的介面設計,才容易撰寫測試程式碼。當你希望盡快完成測試程式碼時,就會試著讓介面簡單,那麼做出來的設計就會夠簡單。

介面簡單化,可以避免設計太早過度工程化(over-engineering),被設計者加入了太多不會使用到的東西。因此,它可以幫助我們減少所需撰寫的程式碼數量。而先寫測試程式,能提供一個壓力,讓設計者專心面對測試程式端直接從規格所傳遞過來的需求。所做的設計,都是直接要解決所面臨的需求,這麼一來,所寫下的實作程式碼,也都是恰好要滿足這些需求的。最終,就減少了所需撰寫的程式碼數量。不過即使如此,這也只能減少實作的程式碼數量,但是想要彌補撰寫測試程式碼所需的時間,是不夠的。那麼,「測試先行」所帶來的改善究竟在那裡呢?

這道理其實很明白。「測試先行」一般來說,能產生品質較好的程式碼。在軟體開發中,測試階段通常會花上和撰寫程式碼同等的時間、甚至常常會花更多的時間。在所撰寫的程式碼品質不夠好時,拖到測試階段,只會付出更多的代價而已,這意謂著,你得花更多的時間才能修正程式碼中的問題。因為,一旦問題到了測試階段才被察覺,問題被回報至程式設計者,程式設計者得花時間進行除錯,接著才能修正問題。

有很多程式上的臭蟲,甚至難以估算能被修正的時間。為了除去臭蟲,你得修改程式碼,而這樣的動作難保不會引起任何的副作用。一旦產生了些許的副作用,到了測試人員面前,可能還是過不了關,於是又再度被退回給程式設計者。如此的過程反覆個幾次,就足以讓耗費在測試及修正問題的時間,遠遠超過撰寫程式的時間。

完成速度快、品質又好才是關鍵
多快能寫好程式碼不是個大問題,重點在於多快你能得到品質夠好的程式碼。

許多人常誤解的是把「測試先行」或「測試驅動式開發」誤以為是先做測試。事實上,這些為了測試實作程式碼的測試程式,並不能取代掉原有的測試階段。即便你有了這些單元測試程式,你的程式也通過單元測試程式了,但你還是需要一個測試階段來進行測試。這些單元測試是用來確保(1)你的程式的確實作了所有的需求,(2)你的程式在單元的層級上,滿足了一定程度的測試覆蓋率。

因此,在需求面上的問題可以被提前偵測,而在程式碼本身的品質,也能因為單元測試程式的協助,而盡可能的提高。或許,它無法減少、甚至會增加撰寫程式碼的時間,但是,它有助於產出較高品質的程式碼,藉以降低日後因為程式碼品質低落,而必須付出加倍代價的可能性。

「測試驅動式開發」是一種開發的步調和節奏,在它的步調下,程式的撰寫者是從撰寫測試程式碼來邁出開發工作的第一步。他可以從撰寫測試程式碼的過程中更深入的了解需求及規格、也從這過程中反覆的淬煉自己的設計,得到更簡化、更易於使用的結果、同時也藉此更全面的思考實作上必須要考慮到的諸般情境。

專欄作者

熱門新聞

Advertisement