[軟體] 淺談測試驅動開發 Test-Driven Development ( I ) 對軟體測試的刻板印象

軟體開發是件很奇妙的事情,一直以來我並未刻意使用任何 方法進行軟體開發,說穿了就是 free style,沒有任何 戰術策略,直到最近找了許多 TDD(Test-Driven Development)的相關資料,才驚覺我對 TDD 其實有很深的誤會

對軟體測試的刻板印象

我是真男人,不需要寫測試

許多程序猿(包含我在內),對於自己寫出的code有一定的自信,但 人非聖賢孰能無過? 你不能保證在疲勞的狀態下也不會出錯(事實上任何狀態下都無法保證)。

然而,當你的code悄悄長出蟲(bug)時,只好摸摸鼻子修正,但其實你也沒有把握這次修正一定完美。

其實你是有測試的(以App開發為例),步驟如下:

  1. 確認錯誤區塊
  2. 修正程式碼
  3. 執行App到該區塊
  4. 確認是否修正 <= 測試

通常你會重複2~4,直到你認為錯誤已被修正

不過你只做了一項測試(你有執行到的部份測試),但你要如何保證這次的修改沒有產生其他的bug?

更精確一點的說法是,如何提早知道其他程式被你此次修改給破壞(borken)導致產生其他bug?

如果你沒辦法保證又怎麼稱得上真男人?

寫測試會花費額外的時間

事實上,不寫測試不一定會比較省時間,上一段提到的步驟3是花費的時間成本是難以忽視的,如果你出錯的部分在App的第1頁,那恭喜你,你很快的就可以走到步驟4,但如果錯誤是在第10頁呢?

此外,你也不能保證code強健性(robust),心理會帶著不確定感把code送出。

最後才寫測試

我一直以為測試是軟體開發接近尾聲時才做的事情,當你把程式全部寫完後,開始針對每個物件(class)及元件(component)做測試,確定軟體正常運作的最後檢查,但這樣的想法只對了一半。

的確,我們在發佈(Release)之前,應該經過 QA(Quality Assurance) 的檢測,確保軟體品質達到一定標準,但QA所能做的事情不外乎是 Web 的 A/B Testing 、 App 的 Monkey test 以及直接的人工功能性測試,當然也有強者QA是會寫code的,他可以做程式化及自動化的測試甚至是單元測試(Unit Test),但強者QA不是到處都有,而願意下重金請強者QA的公司在台灣真的是少之又少。

站在公司角度,讓RD寫測試非常不合成本效益,它既沒辦法開發新的功能,也只不過是降低系統出錯機率; 而站在RD的角度,除了會覺得寫測試枯燥乏味以外,後寫測試有一種自我否定的味道,任誰都沒辦法輕易接受。

在這樣的情境下,最後都會變成,不用寫單元測試,直接進到人工測試。

這段不代表最後才寫測試有什麼不對,只是效益沒有比先寫來的好(後面會解釋)。

一個人寫測試,另一個人實作

每個人都有盲點,藉由 Programmer 相互測試可以提昇程式碼的強健度(robust),但副作用是要花大量時間。

畢竟每個人寫程式的方法不盡相同,寫測試的人必須先弄懂別人負責項目的需求,再看懂他寫的物件,才能順利的寫出好的測試,這個方法很好,但有時候你沒有資源與時間讓你這樣做。

先寫完全部測試才開始實作?

既然不要後寫測試,那我全部先寫總行了吧?

除非你有強健的SA(系統分析師)與SD(系統設計師),否則你沒辦法在軟體開發時 按圖施工,但大多時候需求變更是不會停止的,若你寫完全部Test,上面才跟你說設計改了,相信你會不由得說出那 國際問候語

此外,當你先把全部Test寫完的時候,全部一片紅通通,就比一次丟1000題的考卷給你,即便你知道時間還很多,但士氣都已經去掉一大半了。

寫測試是為了抓bug?

Notice that the purpose of testing is to show taht the product works, not discover bugs.
--Graham Lee

測試的目的是為了顯示產品是可以運行的,而不是找到bug。

The test define the acceptance criteria of the product.
Unless all teh tests pass, the code is not good enough.
--Graham Lee

測試定義了「驗收標準」,除非通過全部測試,否則code都不夠好。

如果你沒寫測試,你要如何證明這個物件正確的寫完了?
-- Liyao Chen

在沒寫測試的情況下,如何確保這個物件已經完成當下你賦予它的任務? 你又如何證明它按照需求運作?

在沒寫測試的情況下,你是如何定義這個物件已經寫完了?

我絕對不會害別人的code壞掉

在前一陣子公司內討論使用git的時候,我很自以為是的說了一句話:「本來就不應該commit任何有問題的code。」然而當時的我並 不能確認每次修改的code不會影響到其他人的功能? (不只是沒改別人code而已,共用的model部分也應該正常運作)

當時我並沒實行 TDD,也沒有做Unit Test,我只能確定在我執行的時候(的部份) 看起來是正常的,並沒有 證據可以幫助我確認 這次修改不會影響其他code功能這件事。

是的,我無法確認我的修改是安全的,除非我把全部程式執行一遍,但那成本太高了,所以當時的我只是個信口開河的騙子

結論

而我的理解是,進行TDD 並不是寫測試(test),而是衡量(measure),寫程式的過程中,透過不斷確認自己的所在之處,並且立即修正,才能 更快速穩定的達到目的地

拿唱歌來舉個例子,free style 的方式是把整首歌唱完之後錄下來給別人聽, 每一次都重唱一整首歌,直到最滿意的那次; 而TDD的方式則是一句一句的錄,偷過快速調整,把小區塊拼湊起來,最後合併為一首歌。

你覺得誰錄的比較快呢? 誰又會比較好呢?

Cost of Fixing Bugs Found at Different Stages of the Software Development Process
上表出自Test-Driven iOS Development --Graham Lee

由表1-1可以看出,bug越早被發現時,修復成本越低,只要是能及早發現,就能及早治療。

然而,TDD只是其中幫助你的 策略或是 戰術,如果你是free style信仰者,你也可以把球交給KOBE就好,剩下的交給自由發揮

當然,這篇不是要告訴你TDD就是萬靈藥,每一個軟體開發的情境 不盡相同,應該要看你手上有什麼 資源來決定最佳的方式。

但在你決定要不要用某個策略之前,你應該要先徹底了解一番,分析利弊後在選擇你要的戰術,在對的時間點發揮最大效益。

以上是個人小小淺見,如果有誤請大家多多指教。

Reference