《Python Web開發》作者Harry Percival:TDD就是微小而漸進的改變(圖靈訪談)

盼盼姐發表於2016-02-18

Harry J.W. Percival目前就職於PythonAnywhere,他在各種演講、研討會和開發者大會上積極推廣測試驅動開發(TDD)。他在利物浦大學獲得電腦科學碩士學位,在劍橋大學獲得哲學碩士學位。Harry Percival著有《Python Web開發:測試驅動方法》一書,該書手把手教你從頭開始開發一個真正的Web應用,並且展示使用Python完成測試驅動開發(TDD)的優勢。你將學到如何在開發應用的每一個部分之前先編寫和執行測試,然後再編寫最少量的程式碼讓測試通過——也就是應用TDD理念,寫出簡潔可用、賞心悅目的程式碼。

enter image description here

問:很多敏捷教練都表示訓練新人做測試驅動開發(TDD)是一件辛苦而進度緩慢的事,並且收益也不是很大。你在《Python Web開發》中採用了哪些方法來避免這種情況?

這是一個很難的問題!教授TDD這樣的方法是無法保證收效的,但是既然市面上有那麼多關於TDD的書和教程,也就證明了人們一旦理解了這種方法,他們的程式設計方式就會發生改變,而新的方式會令他們感到更加欣喜,所以他們就有了分享的慾望。

讓我的書區別於其他書的地方我認為主要有兩點。首先是《Python Web開發》的“對話式”風格。與其花氣力寫一本學術性、講座式的書,我在寫作本書時,想象的是讀者坐在我身邊和我一起完成一個專案的情景。我就是這樣學習TDD的:和專家結對程式設計。所以,我沒有選擇發表一系列課程或規則,而是通過真實的例子來演示如何使用TDD來解決問題,同時我還會告訴讀者我腦子裡想的是什麼,我是如何做決定的,為什麼我會使用某種測試方法。

第二點不同在於,我選用了非常實際的例子——真正的專案,用來構建具有真正功能的真正網站。雖然簡單,但是很真實。不像很多教程那樣,比如讓你搭建一個“羅馬數字翻譯器”或其他抽象例子,雖然這樣的示例乍一看讓人覺得清楚明瞭,但是你很難把這樣的東西應用到真實世界中。

問:從你的角度上說,學習TDD最有效的方法是什麼?TDD的學習曲線是否陡峭?

可以這樣說,TDD的理論很簡單,但是實踐很難。只要通過一個簡單的例子就能輕鬆理解TDD的基本思想,你在任何線上教程中都能找到。但是把這種方法應用到真實世界的專案中可就難了。只有時間的磨礪才能讓你在判斷測試的好壞時找到感覺,什麼是有用的,什麼是沒有成效的。遺憾的是,獲得這種知識的唯一方法就是時間和經驗,但是我會盡力幫助讀者在正確的路上跑起來。

在這《Python Web開發》中,我努力讓學習曲線儘可能地柔和——我每次只介紹一個概念,並且讓前幾章的內容短小而令人愉快,我堅持每章一個概念為的是讓讀者理解基本概念,哪怕我們開頭學到的只是經過簡化的不切實際的技巧。到了後來我們會開始學習“真正”的方法,這些技巧會稍微複雜一些。相比於提供一步到位的完美解決方案,我認為這種學習方法更好。在書的最後,讀者將會接觸到我和我的同事們每天都在使用的最為真材實料的TDD技巧。

問:你為什麼選擇Python作為講解TDD的程式語言?Python是否具有某些其他語言無法比擬的獨特優勢?

我傾向於把Python作為教學語言是因為Python非常簡單——它“不礙事”。Python程式碼非常易讀,而且沒有很多礙事的樣板;沒有你在Java中會看到的“public static void main %#¥%%¥”關鍵字。這些東西可能對於大型企業級應用是有效的(沒有定論!),但是在教授程式設計方法的過程中卻是不折不扣的絆腳石。的確,Python簡單,利於教學,但是Python也很靈活,這也讓很多TDD技巧變得簡單了(比如模擬和修補,我在書中將會講到)。

問:就Web開發而言,Python Web和ASP.NET Web有什麼區別?它們各有哪些優缺點?

我必須承認,我對ASP.NET一無所知,所以我也沒什麼資格回答這個問題。我知道很多業務之所以使用了ASP.NET是因為它是某種常見的微軟棧的一部分,但是我也知道很多矽谷創業公司都藉助Python獲得了巨大的成功(比如Youtube, Instagram, Reddit,等等)。

問:你認為Flask怎麼樣?你在Django和Flask之間如何抉擇?

Flask很簡單,但是Django有更多內建功能(或者我們有時會說“自帶電池(功能齊備)”)。我把Flask用在了小型“微服務”應用上,但是對於大應用來說Django是一個很不錯的選擇,因為Django擁有很多強大的特性,比如認證、管理介面,以及各種第三方外掛和應用。但是就像任何“一體適用”解決方案一樣,有時效果很好,有時內建Django功能卻並不是你真正想要的。所以要有所權衡,但是到最後,這個決策也沒有那麼重要。無論你用哪種工具都可以獲得成功!

問:業務越複雜測試越多,有時甚至比開發時間還長,這樣的情況對於小型創業公司來說肯定是不願意看到的。關於如何平衡測試和開發,你有什麼建議?

我想先退一步來論證這個問題。但是首先必須要說明,我認為複雜的專案並不一定意味著“成比例”的測試時間。但是為了討論需要,我們先假定一段帶有測試的程式碼需要兩倍於沒有測試的程式碼的時間(雖然有些人會告訴你,如果你很牛的話,寫帶有測試的程式碼會比沒有測試的還快)。但是我們還是假定寫測試會多花一倍的時間。如果為一個小型的簡單專案寫程式碼需要花費兩倍的時間,那麼為複雜的大型專案寫程式碼也要花兩倍的時間——比例不會變。

區別在於測試帶來的好處。在一個簡單的專案中,測試並不是特別有價值的。如果你的專案很簡單,你可以輕鬆快速的完成手動測試。如果你想對程式碼進行大改動的話,比如升級核心模組或者重構中心演算法,那麼你不需要測試就能完成,只要花很少的時間你就能檢查完畢,並且自信一切沒有問題。

但是對大專案來說,就是另一碼事了。想象一下你參與過的最大的專案,並且想象一下升級一個核心元件的情況——該框架絕對會出現在所有程式碼中,而你想為這個元件升級一個帶有新API和新特性的新版本。沒有測試,這將是一個幾乎無法完成的任務。在最近的工作中,我們升級了我們的Django核心版本,多虧有測試,我們只花了幾星期時間就完成了。到了最後,我們得以確保一切都能順利執行,而且當我們把專案部署到客戶端時,我們也有信心客戶不會看到迴歸。但是如果沒有測試的話,我們就無法完成。可以毫不誇張地說,如果沒有測試我們連試都不會試,因為風險太大了。而且我們就得一直將就著用老版本Django,沒準這個版本最終會老到不被支援的程度,並且還會產生各種各樣的安全問題……

所以隨著時間推移,到頭來還是複雜的專案會收穫TDD真正的好處。沒有測試,你將不敢在你的系統上做太大的改動。你會迴避重構,因為風險太大。技術債越積越多,做出新改動會越來越難,而專案上的工程師則會越來越悶悶不樂。

我在《Python Web開發》的前言中談到了我在第一個專案中的經歷。

在實踐測試的過程中,這是最難的部分之一——這是一種投資,需要花費額外的時間和努力,雖然可以即刻提升程式碼,但是“真正”的好處卻是在未來才能兌現的。所以只有團隊和管理層具有前瞻性思維才能完成。雖然速度對於創業公司來說至關重要,但是如果公司因為欠下太多技術債而無法繼續創新和響應市場需求的話,創業最終會走向失敗。

問:在測試驅動開發中,每一個類,每一個方法都需要寫測試用例嗎?如何在不花費太多時間的情況下做到面面俱到?

我在書中的第4章談到了這個問題。你需要找到自己的平衡點。你可以為自己辯護,如果一個功能規模很小而且無關緊要,可能就不需要測試了。但是危險在於:隨著時間推移會發生什麼?可能這個簡單的功能會變得稍稍大一點,加了一個if語句。但是這個功能沒有測試,而增加新測試就需要額外的努力,所以加入沒有測試的新程式碼可能也沒有關係。然後這個功能增加了一個for迴圈,一點一滴,變得越來越複雜,也越來越需要測試,但是由於每個階段的心理屏障,這個功能可能永遠都沒有測試。所以也許最安全的做法就是在最開頭的時候做測試,因為在已經有了測試的情況下增加新測試就會容易很多。畢竟,如果功能真的很簡單的話,寫第一個測試應該也難不到哪去吧?

問:我們知道測試驅動開發在小專案中是非常有效的,但是TDD是否適用於中型甚至大型專案?在大型專案中使用TDD時是否有哪些需要注意的事情?

正如我所說的,TDD的真正好處只有在專案達到一定規模和年限時才會顯現出來。但是沒錯,確實有一些問題需要注意。第一個問題就是測試套件的速度。這件事很容易就會被做過頭,而且有些人執著於測試速度,但是這個問題還是很有必要注意一下的。如果你有測試分散在低階“單元”測試和高階“功能”測試中,那麼你應該擔心的問題是:你的單元測試的執行時間何時會變成幾分鐘,或者功能測試何時會變成幾小時。測試方法的核心就是在合理的情況下儘可能快地得到反饋。有時你會發現,你的測試時間不再隨著應用的大小成比例增加,而是隨著複雜度增加出現幾何級增長。但是這個問題是可以解決的。我在《Python Web開發》後面的章節中談到了如何取得平衡,特別是最後一章(第22章)。

問:遺留程式碼通常對於測試來說不太友好,如果要把TDD應用在遺留程式碼上,你有哪些建議?

我不能說自己是這方面的專家,但是關於這個問題Michael Feathers寫了一本很不錯的書,叫做《修改程式碼的藝術》。總體的建議是:不要絕望。在大量的已有程式碼庫上增加測試可能看起來像個不可能的任務,但是這件事是可以完成的,只要隨著時間循序漸進就好。一旦你決定開始增加測試,那麼你就可以選擇在每次向程式碼中加入新需求時增加測試,或者在每次出現需要修理的bug時增加測試。隨著時間推移,測試覆蓋量將會增加,而你得以開始重構應用,使其測試性更強,並且加速這個過程。

問:前端的測試驅動開發是否可以擴充套件到後端?軟體工程中是否有哪些模型值得推薦?

在書中,我談到了使用不同的測試層來測試前端(呈現在真實瀏覽器中的HTML渲染和Javascript效能)和後端(連線資料庫並且處理資料的控制器和檢視)。通過使用“高階”層的“真正”測試,我稱其為功能測試,通過Selenium我們可以測試前端和整體應用。低階“單元”測試被用於測試前端Javascript和後端Python程式碼的單獨元件。在《Python Web開發》的第18章和第19章中,我談到了這方面的問題,這種模型名為“雙迴圈TDD”和“外向內TDD”。

另外,我需要宣告這本書並不只是關於TDD的。這本書講的是一種用結構化、嚴格而且漸進的方式來做軟體工程的完整方法。在我剛開始程式設計時,我只是在“碼磚”——靠自己找答案,用“牛仔style”程式設計,為了讓程式執行,我經常走捷徑。最開始這樣做沒什麼問題,但是隨後你就會陷入麻煩。

在接下來的幾年中我逐漸學會利用工具和技巧、以結構化的方法來程式設計,這就是我想在這本書中傳達的。我說的是TDD,沒錯,但是也包括很多其他東西:使用Git這樣的版本管理工具並且完成無數微小的漸進式的提交。雖然聽起來像是毫不相關的主題,但事實上這是和測試驅動的工作方式緊密相關的,這種方式的核心就是微小而漸進的改變。我演示的一些“DevOps”思想講的並不僅僅是構建和測試你自己的機器,同時也關於如何搭建登臺環境,以及如何配合自動部署指令碼使用測試來確保程式碼在生產環境下、伺服器上、網際網路上,以及本地計算機上順利執行。我也講到了如何整合第三方系統,以及使用持續整合伺服器在一夜之間執行構建,我還談到了一點關於在敏捷開發方法(如XP)中使用TDD的方法……我努力想要把我知道的所有“碼磚”和“真正的軟體開發”之間的區別,通過一本書傳達出來。


更多精彩,加入圖靈訪談微信!

相關文章