Blue pill or the red pill
(圖片來源︰Sarah Fagg)

2014/04/28 Updates

  1. 補上 DHH 原始 keynote 的影片︰RailsConf2014 Opening Keynote by DHH
  2. Uncle Bob 針對 DHH 的戰文,也寫了一篇文章來反擊︰Monogamous TDD
  3. XDite 在 4/28 也再撰文整理他的看法︰返璞歸真 -- 以最適當的方式設計軟體

前言

昨天在 XDite 大大的 Blog 中 (via: RailsConf 2014 - 十週年紀念版 ( 中 )),看到 RoR framework、Basecamp 公司的創辦人、Rework 一書 (中譯書︰工作大解放) 的作者,David Heinemeier Hansson (DHH),在 RailsConf 2014 中的 Keynote 後,發表了一篇針對 TDD 的戰文︰TDD is dead. Long live testing.

其實我覺得 XDite 整理 DHH 的 talk 重點就非常完整了,雖然我自己也是 TDD 的愛好者,但像這種由業界大神所寫出來的戰文,我覺得都有很高的閱讀價值。就算對方立場和你是相反的,從對方的論點中也可以找出滿多的檢查點,來反思自己在實踐上,是不是也犯了他們所討厭的錯誤,或是從錯誤的出發點來想事情。

所以我翻譯了這篇文章,除了讓自己再精讀一下 DHH 的想法外,也讓有興趣的朋友們可以多了解不同的意見,做為反思。

有任何翻譯上的謬誤,都歡迎提出,謝謝!

翻譯正文

TDD 已死,測試萬歲 (TDD is dead. Long live testing.)

先寫測試的教義就像是禁慾式的性教育︰一個只會帶來自我厭惡和羞愧地,不現實和無效率的道德宣傳。

一開始測試先行的想法並不像現在這樣。當我一開始接觸 TDD 時,它就像是個想帶領我們進入更好的軟體開發世界的友善邀請,一種對心靈的激盪 (hack),來讓你進入測試的實務。它擴展了我的眼界,讓我了解經過良好測試的程式庫的美好,並且對於軟體的修改更有自信。

測試先行就像是一套很棒的輔助輪 (譯註︰training wheels,就是小朋友學單車時在後輪加裝的輪子),教我思考更深層次的測試議題,但當中也有一些想法被我很快地拋在後面。

這些年來,關於測試先行的言論變得更加大聲、更加激進,然而,也更加失去風度。有時候我會被吸進了基本教義派的旋渦 - 對於沒有依循宗教的福音行事感覺到很糟糕。然後,我會在接下來的幾週,嘗試使用測試先行來進行開發,直到它開始傷害我的設計後,又再度捨棄它。

這個過程很像溜溜球在自豪的兩側擺盪︰當我可以堅持基本教義的一字一句時,我感到自豪;而當我做不到這點時,我會絕望地崩潰。這就像是原本已經戒酒的人又開始喝酒,你當然要為(喝酒)這事保持沉默,至少不能公開地向大家承認這事。在公開場合,我充其量只是承認自己不是每次都使用測試先行的方式進行開發,但仍然支持測試先行這種實踐 (practice) 是「正確的做法」。我現在要收回這段話。

也許使用測試優先這種違反直覺的開發方式,對於打破業界缺乏自動迴歸測試的生態是有幫助的。但也許測試先行只是一種比喻,而不應該成為軟體開發工作的每日工作教條。但不管它是怎麼開始的,它會因為損壞而只是曇花一現。支持者常使用測試先行當做一個錘子,來打擊懷疑論者,宣告他們不夠專案,不夠格來撰寫軟體,就像是試紙檢驗一樣。

夠了!這種事不應該再繼續下去!我叫 David,我不以測試先行的方式開發軟體。我拒絕為此表達任何歉意,也不想再為此隱瞞。我很感激 TDD 為我打開了關於自動化迴歸測試的眼界,但我早已擺脫設計教條而繼續前行。

我建議大家在不使用 TDD,不表示這是不合格的開發方式的可能性的前提下,認真檢視這方法(TDD) 會對您的系統設計完整性帶來了什麼影響。這就像是服用紅色藥丸一樣,您可能不會喜歡之後您所看到的 (譯註︰這邊的出處是電影「駭客任務」,當初莫斐斯就是拿了紅色藥丸和藍色藥丸給尼歐選擇要不要跳船)。

所以接下來我們該何去何從呢? (So where do we go from here?)

第一步是承認問題。我認為現在我們已經做到了這一步。第二步是重新平衡從單元測試到系統測試間的頻譜 (spectrum)。目前狂熱的 TDD 的經驗導致了在測試上都聚焦於單元測試,因為這是可以驅動代碼設計的測試(這也是最初社群鼓吹測試先行的最初理由)。

我不認為這是健康的。先寫單元測試導致我們產生了一堆複雜的中介物件作和間接層,以避免讓單元測試太「慢」。像資料庫存取、檔案 I/O、或是通過瀏覽器來測試系統,都會被隔離出來(譯注︰一般建議每個單元測試的執行時間不能超過 500ms,另外不建議單元測試和外部資源 - 像是 DB、檔案系統 - 相依。為了達成這目標,我們常會將待測物件 A 的相依物件 B、C、D 以介面隔離,在單元測試時,使用像 Mock/Stub 的方式來注入假造的物件 B、C、D 給物件 A。一方面讓物件 A 不會存取外部資源,另一方面也讓整個單元測試夠快)。這種方式帶來了一個真正可怕的怪物架構︰一堆複雜的服務物件、命令模式或其他更糟的東西。

我很少撰寫在傳統意義上的單元測試︰假造所有的相依物件,來讓上千個測試可以在幾秒內測試完畢。因為它對於測試 Rails 應用程式並不是一個有用的方法,我會直接測試 active record model,讓它們透過 fixture 來存取資料庫。然後以這為基礎,在上層運行一組對於 controller 的單元測試,但對我來說,我更願意使用像 Capybara 或類似的系統測試工具,來取代這些單元測試。

我認為這是我們應該前進的方向︰減少對於單元測試的重視。因為我們不再做測試先行的設計實踐,而是更加重視 - 喔,是的 - 執行速度更慢的系統測試。(順便補充一下,由於平行化和雲端基礎建設的成熟,系統測試其實也不會比單元測試慢得這麼多)

Rails 可以幫助大家做這種轉移。今天我們不鼓勵完整的系統測試,在開發的堆疊中也沒有預設的答案,這是一個我們將要修復的錯誤,但您不必等到這件事發生,讓 Capybar 從今天就介入您的開發週期,如此一來您將會有我們正朝著美好明天的正面思維。

但首先,讓我們先深呼吸一下。我們正在驅趕一些神聖的奶牛進屠宰場,這是痛苦和血腥的過程。TDD 是如此成功,以至於它結合了許多程式設計師。TDD 所代表的是不只是它在開發方法上的技法,還有背後以此技法開發的從業人員。(TDD is not just what they do, it's who they are.) 我們必須消除在我們面前巨大社群的思考慣性,而這將需要一些時間。

我們能做的最糟糕的事情是再跳到另一個關於測試的宗教,我可以想像那個新宗教的標語是「只做系統測試! 」。請不要再度跳到那個坑去。

是的,測試優先對我來說已經死了。但比起在它的墳墓上跳舞,我更想做的是向它的貢獻表達我的敬意。它在我們的(開發方法論)歷史上占有一個重要的階段,但現在是時候讓我們繼續前行了。

測試萬歲!(Long live testing.)