筆者從業數年,面試過不少程式開發者。每當問到對方是否有做單元測試時,絕大多數的面試者總會說:
「我知道測試很重要,但我們沒時間寫測試。」
「我知道測試可以避免不必要的錯誤,但我們沒時間寫測試。」
「我知道測試可以加速開發,但我們沒時間寫測試。」
羅蘭夫人,圖片截自網路
『沒時間啊,沒時間,天下古今幾多之罪惡假汝之名而行!』
你都不測,你怎麼知道你做的功能是對的?你怎麼知道你的新修改沒有把舊功能搞壞?你怎麼好意思告訴老闆你「做完」了?
我們來看看一間公司,如果 RD 只開發,QA 只測試,會發生什麼事情?QA説,我的工作就是抓 bug,我要盡可能地多抓 bug,這樣才能顯示我的工作能力好。所以 bug 越多越好。
RD 呢?RD 説,我的工作就是寫程式,所以我收到需求就「熱熱寫,快快送」,頂多自己手動稍微測一下,反正 QA 會細測,有錯他會跟我說。我所有的工作要盡可能地快速經手,這樣才能顯示我的工作能力好。雖然這樣 bug 會變多,但我無妨,那不是我的 KPI。
有發現嗎?一間公司,如果 RD 只開發,QA 只測試,造成的後果就是「沒有人有動機去避免 bug 產生」,接著就是品質無法得到保證。
軟體管理常見的取捨,都是在「時間」、「成本」、「品質」當中挑選兩項,並且時常把「你只能滿足其中兩項」掛在嘴邊。然而,事實真的是這樣嗎?在我看來,「品質」不應該是選項,因為品質一旦犧牲了,你會直接失去消費者的信任,這下你時間再快,成本再低都沒用。
軟體管理三元素,圖片截自網路
真要我說,你應該要首先先讓品質維持在一定程度以上,然後再視情況在另外兩個因素中選一個才對。為什麼不說品質要高,而是「一定程度以上」呢?你知道的,哪有人寫程式沒 bug 的?只是多或少,好不好抓,及抓到好不好修的差別而已。這,就是測試可以發揮的地方了!
很多人都說電腦科學是數學,但也有很多人不這麼想。數學跟科學都追求正確,但方法不同。數學要求你「證明」,透過一些邏輯的交錯應用,來證明一件事 100% 是對的,無論日換星移,這件事都不會變。但科學不同,科學追求正確的方法是「實驗」。科學家先針對一個現象提出一個假說,經過巧妙的安排,試圖重現這個現象。幾十年幾百年過去,無數次的實驗結果,都沒人推翻這個假說,那我們就相信這個假說是正確的。
對此,Uncle Bob 曾說:「電腦科學是科學,不是數學。給你一段程式碼,要你證明他的正確性成本太高了,幾乎辦不到,但你可以做實驗驗證他,並且無數次不斷重複這個實驗,當你實驗次數夠多,大家就相信這段程式碼是對的了。」
這裡的實驗,就是「測試」。
這就是我們為什麼要測試。我們給出一段程式,我告訴老闆它是對的,老闆憑什麼相信你說他是對的?因為 CI/CD 工具的儀表板上明明白白顯示出,過去這段程式碼跑過無數次測試,從來沒有出錯過,因此他應該要相信這段程式碼是對的。
測試有分很多種,而一切軟體工程的根源,就是最基礎也最單純的「單元測試」了。Martin Fowler 曾經拿單元測試與整合測試比較,並給出他認為的定義:「能夠獨立於所有其他服務或元件而獨立運行的測試,就是單元測試,而不管是不是有使用 mock 或 stub 技術...單元測試關注『我們的功能跑起來情況如何』,而整合測試關注『我們的功能與其他元件合作跑起來情況如何』。」
如此一來單元測試定義就簡單多了:「驗證我手上敲的程式的表現跟我腦中想像的是否一致」。
如果寫程式的人都能夠附上能佐證正確性的單元測試,那麼確保功能性就簡單多了,因為現代的 CI/CD 工具太厲害了,可以隨時隨地觸發,你甚至不用盯著螢幕看,結果出來他會自動通知你。事到如今,能阻礙你寫測試的只剩下「你寫得多快」了。至於要怎麼快,那就要看你做得多好了。對此,Uncle Bob 都說了:「你唯一能把事情做快的方法,就是把它做好。」
本系列文章圍繞著單元測試,將會包含以下軟體工程概念:
讀者從上述列表中不難看出,大家平常常聽到的諸多軟體工程實踐,大多都與單元測試有關。應該說,他們大部分都必須建立在單元測試之上,才能有比較好的發揮。
本系列文章中,需要時都會有程式碼輔助理解。讀者可以直接下載下來閱讀原始碼,或是參考著練習。搭配文章一起服用,可達到理論與實踐並重的成效。作者期待的效果是,讀者未來在掌握訣竅後,在工作上真正能靠它來「加速」,如此一來,想要寫測試,就不用非得老闆多給時間不可了。
謎之聲:「那我們就開始吧!」