本節書摘來自異步社群《編寫可測試的javascript代碼》一書中的第1章,第1.1節,作者: 【美】mark ethan trostler 譯者: 徐濤 更多章節内容可以通路雲栖社群“異步社群”公衆号檢視。
每個人的想法都是獨一無二的,但代碼不是。幾乎每個行業都完成了機器革命,然而奇怪的是,計算機科學行業并沒有。程式員基本上是在做已經做了40年的同樣的事情。手工編寫代碼,接着這些代碼被編譯或解釋,然後執行。看看輸出結果,再确定是否需要再改代碼。計算機科學的黎明到來之前,這種開發周期就一直這樣保持不變。我們的機器在幾何級變快,記憶體和二級存儲大小是無限制的,且這樣發展下去軟體越來越複雜。但我們仍然一個字母一個鍵地手工編寫着代碼。我們依然濫用着“print”語句輸出運作時發生的内容。我們的開發工具确實越來越強大,但每個熱門的新語言出現時,又會出現新的工具。歸根結底,在一個令人難以置信的自動化世界(并且大部分的自動化都是軟體程式設計的成果),軟體程式設計卻依然是一個幾乎完全手工的過程。一次敲一個字元的軟體程式設計做法仍停滞不前。
雖然我們編寫的大部分代碼之前都已經編寫過,可能是目前正在使用的語言,或者之前用過的語言,但每個應用程式都是獨一無二的,即使我們和競争對手都在做同樣的事情。不管是否獨特,成功的應用程式是必須能夠使用的。它不需要漂亮,也不需要絕對得快,或者也不需要最豐富的功能,但它必須得能用才行。
應用程式的核心,隻是擁有輸入輸出的消息傳遞系統。而在此之上建構的複雜性越來越大。由于javascript的出現,我們不僅要吸取其他語言的經驗教訓,還要從javascript自身吸取教訓以便編寫可測試的代碼。在用戶端和伺服器端,javascript的應用程式都越來越大,我們必須非常謹慎地應用前輩們提供的最佳實踐和經驗教訓,加以調整以很好地适應javascript代碼。
圖1-1顯示了過去三十年每個半導體周期的微處理器成本[1]。這幅怪誕的圖表示了每個cpu的周期成本,此圖遵循摩爾定律,有不可避免的下降趨勢。硬體重新整理率确實遠遠超出任何軟體層面。

通過程式設計機已經将對象變得更快更小以獲得巨大的利益。為了達到不可思議的全球規模,大批的機器通過實施标準化組裝于工廠中。然而軟體工程師卻仍然要在他們的個人電腦面前坐着,在鍵盤上不停地敲打着每個字元。
編寫軟體雖然是一個手動過程,但已經有很多人嘗試編纂和規範開發人員應該如何做事,以便為建構“良好”代碼建立一個可重複的過程。當然這些過程,希望引導固執的開發人員編寫“整潔”和“零bug”的代碼。然而在大多數情況下,本書下面幾節中采用的過程或方法,其結果直接取決于開發人員在系統裡引入的意願。本書内容不關注技術的選擇或使用,而是關注程式設計實戰中應該考慮做什麼。讓我們通過如下一些内容思考一下。
1.1.1 靈活開發
靈活開發是大量實踐中一個比較大的方法。靈活方法主要是應對軟體應用程式開發的“瀑布”模型,瀑布模型開發使用離散性的序列化過程。例如,瀑布模型首先是需求規範編寫,接着程式員編碼、測試人員測試、應用程式部署,然後回頭重新更新新的需求。該過程的每一步都是連續且獨立的。是以,編寫需求規範時,程式員和測試人員都要等待,程式員編碼時,測試人員再等待,而測試人員在測試的時候,所有人都在等待,等等。
靈活開發嘗試以更加靈活的方式讓每個階段都并行發生。軟體能用是首要任務,與其花大量的時間等着上一步的完善交接,不如每個團隊進行短周期的疊代,以便時刻都有事做。大量的工作被分解成可以預估的小塊。靈活開發嘗試打破開發周期内每個小組的壁壘,這樣他們可以一起工作,進而減少互相之間交接的時間。與客戶溝通有助于确定最終的傳遞成果。
注意,使用靈活開發方法,并不一定意味着應用程式完成得更快且品質更高。靈活開發的最大優勢是它處理需求變更的方式。在瀑布模型中,任何改變都需要貫穿整個過程。靈活開發的短周期允許變更,以便将這些變更整合到最終的産品中。如果你聽說過fail fast、release often、backlog、standup或者和continuous相關的詞語,你可能已經在使用靈活開發了。某種程度上,大多數現代開發都使用靈活開發。圖1-2顯示了靈活開發過程的典範圖。
圖1-2中有很多内容,但基本思想是:快速疊代和持續互動可以加快高品質軟體的傳遞。
靈活開發本身沒有提到如何編寫軟體,相反,它建議了一些适合靈活哲學的方法。例如,“使用者故事(user story)”是由“使用者(user)”描述一個應用程式該有的特性的句子。這些故事是使用者想在應用程式中展現的産品特性。使用你的應用程式或api的使用者,可以是普通的使用者,也可以是能夠幫你定義最終産品的另外一組開發人員。結對程式設計是一種經常與靈活開發一起使用的開發方法。結對程式設計最純粹的形式就是兩個程式員坐在同一張桌子旁,看同一個螢幕、鍵盤以及滑鼠,一起進行編寫軟體。其中一個開發人員敲代碼,而另一個開發人員則積極調試和思考代碼。兩個大腦通常比一個更聰明,與兩個人單獨進行編碼相比,結對程式設計能夠更快地發現并解決問題。
1.1.2 測試驅動開發
測試驅動開發(tdd)是靈活軟體開發的推薦做法。tdd希望在編寫代碼之前先編寫測試。這些測試提供了必須遵循預期功能的代碼。編寫測試失敗後(剛開始還沒編寫代碼肯定會失敗),接着開始編寫代碼,以便確定測試能夠通過。保持測試領先于開發,永遠不會有未被測試的代碼,至少這是tdd的理論。在現實中,往往開始時,開發人員會沿着這條路走,并開始編寫測試,但很快功能代碼就會超過測試代碼。呵呵,至少你得到了一些測試!
在建立項目或子產品時,使用tdd的效果顯然是最好的。如果隻是需要單元測試,那它也是最成功的。在編寫代碼之前,編寫完整的內建測試是令人望而生畏的!tdd也為重寫現有遺留代碼提供了一個很好的理由或借口。如果開發人員在為“已存在的代碼編寫一大堆測試”和“在編寫自己新代碼的同時進行測試”中做選擇,大多數開發人員可能會選擇後者。當然,開發人員不一定隻有一個選擇,如果對現有已存在代碼編寫測試是下一步的話,那就不要指望他們能有開心的笑容以及很高的效率。
無論如何,tdd不是一件壞事;事實上,它可以是一件非常好的事情。tdd是一個偉大的開始,不管是整個應用程式還是一個單一子產品—所有人都喜歡編寫新代碼,如果編寫新代碼之前的“成本”僅僅是先編寫測試的話,那就随它去吧。因為開始時沒有代碼,是以編寫測試的“成本”是最小的。
在2005年針對加拿大的大學生開展的一項有趣的研究發現,tdd使程式員更有效率,因為他們寫了更多的測試。雖然這很有争議,但更有趣的是,研究人員也觀察到,随着編寫測試數量的增加,軟體品質很少呈線性提高,這與采用的開發戰略無關。最好是要知道,測試的數量和更高的代碼品質是成正比的。從中能得出的結論是,任何能讓開發人員在編碼前、編碼中、編碼後進行編寫測試的方法都是非常好的方法。
1.1.3 行為驅動開發
行為驅動開發(bdd)是在tdd的基礎上發展而來的,它為開發人員和非開發人員提供了一種通用語言,用于描述正确的應用程式行為和子產品行為,該通用語言是日常語言。例如,提供用于定義被測試子產品的行為的描述,比如“如果購物車是空的就不能進行結賬”,而不是寫一個名為testemptycart的測試。使用通用語言定義的測試或預期,可以讓任何人都能更容易地了解被測試的内容,并且有助于定義測試和期望應該是什麼樣的[2]。
相對于代碼,bdd是利用靈活使用者故事來定義測試。使用者故事可以直接轉化為測試。使用者故事通常必須遵循特定的模闆形式:作為一個[人/角色],我需要[某些功能或權利],以便能[得到相應利益或達到相應的結果]。
正确填寫每個空白項,如作為一位yahoo!mail使用者,我想将照片附加到電子郵件中,以便我的收件人都可以看到它。這個使用者故事可以轉化為yahoo!mail産品的一組功能需求和測試。
bdd對于獲得非項目團隊成員(技術人員或非技術人員)的正式回報很有用,進而幫助你了解系統應該如何操作。使用者故事通常可以直接轉化成測試—或者任何可以促進集中測試(更多測試)的内容,這是一件非常好的事情!
1.1.4 哪種方式最好
本書既不提倡也不解釋任何一種開發方法,從這一點上講,我認為它是成功的。瀑布(waterfall)、螺旋(spiral)、靈活(agile)以及其他方法都很好用,但都沒有産生可測試的代碼,更不用說可測試的javascript了。同樣,tdd、bdd以及其他形式的開發方式也不一定確定有可測試的javascript。怎樣才可以確定生成可測試的javascript?確定編寫出整潔、松耦合并有足夠注釋,且確定别人能夠接手維護的代碼,才能夠編寫可測試的javascript。編寫、閱讀和維護可測試的javascript并不一定需要測試驅動、行為驅動或任何其他“驅動”型的開發方式。然而,遵循任何強調與編碼一起測試的實踐是一件有益的事情。要謹記的最重要的事情是我們編寫的代碼并不是憑空存在的。前面編寫的任何專業代碼,都将被我們自己或者别人查閱、編譯、調試,并最終使用。最後,我們編寫出代碼,讓别人來維護、研究和使用。