javascript
测试驱动javascript开发 -- 4.测试驱动开发过程(下)
TDD是一個迭代的開發(fā)過程,他包括下面的步驟:1.編寫測試;2.運行測試,觀察失敗;3.確保測試通過;4.重構,減少重復。
每次迭代中,測試就是規(guī)范。在我們完成開發(fā)之后,測試就可以通過了。之后我們就需要進行減少重復代碼和提高設計的重構工作,然后再次運行測試,并保證其通過。
雖然TDD不主張預期的大設計,但是我們在TDD開始之前還是需要做個簡單的設計。我們要如何寫自己的第一個設計呢?當我們獲得了足夠信息可以制定測試的時候,測試代碼的編寫,本身就是設計。我們指定在特定情況下特定代碼的行為,系統(tǒng)之間組件如何響應,以及他們之間如何組合。下面我們會舉例,以便大家更好的學習。
TDD中的迭代時間很短,我們需要非常清楚我們所處的階段。無論何時我們對代碼進行了修改,或者移出了某些功能,我們需要把他們記錄在todo列表中,加以觀察。這個列表可以是一張紙,或者記事本之類的東西,只要方便你快速的查找和記錄既可。在處理這些新的修改點之前,我們需要首先結束本次迭代。本次迭代結束之后,我們再從todo列表中取出一個任務,開始下一次迭代工作。
?
步驟1:編寫測試
每次TDD迭代,首先要做的事情是選擇一個你要做的功能,為它編寫單元測試。單元測試需要簡短,測試聚焦在function上的某一個功能點上。比較好的編寫測試的規(guī)則是,編寫盡量少的測試代碼就能讓測試失敗。當然,測試斷言不能和之前的測試重復。如果一個測試涉及到系統(tǒng)的兩個或兩個以上的方面,就說明要么是這個測試太大了,要么是里面包含了重復的測試點。
測試需要能夠描述我們實現(xiàn)的功能,我們的功能代碼沒做修改,就不需要修改測試代碼。
假設我們要完成一個String.prototype.trim函數(shù)的開發(fā),用以去除字符串前后空格。對該方法好的測試,第一步應該是測試前空格是否刪除了。
testCase("String trim test", {"test trim should remove leading white-space":function () {assert("should remove leading white-space", "a string" === " a string".trim());} });嚴謹起見,我們需要先判斷字符串包含trim方法。這是因為我們添加的全局函數(shù)可能會和第三方代碼發(fā)生沖突,在代碼之前添加 typeof "".trim == "function",可以幫助我們在運行測試之前發(fā)現(xiàn)問題。
為單元測試提供輸入條件,運行之后他會判斷輸出條件是否和預期一致。輸入條件不僅僅是函數(shù)的參數(shù),任何函數(shù)的依賴項,例如全局作用域、某些對象的特殊狀態(tài),這些都是輸入條件。與此類似,輸出結果包括返回值、全局作用域或者相關對象。我們通常把輸入輸出項分為直接和間接兩種。直接項:函數(shù)的參數(shù)和返回值;間接項:不是以參數(shù)形式傳入的參數(shù)和被修改的外部對象。
?
步驟2:觀察失敗的測試
測試準備好之后就可以運行了。有很多原因促使我們在編寫實現(xiàn)代碼之前運行測試,最主要的一點是我們可以用它來確定代碼的狀態(tài)。在寫測試的時候,我們會有一個比較明確的期望,測試如何會失敗。單元測試雖然不存在邏輯的分支,代碼也比較簡單,但他也像其他代碼一樣存在bug。但是運行它,比較期望結果,我們會很快發(fā)現(xiàn)這些bug。
理想情況下,當我們添加了新的測試的時候,我們應該可以運行所有測試用例。這樣我們就可以很容易的抓取到干擾測試,例如一個測試依賴于另外一個測試。
編寫實現(xiàn)代碼之前運行測試,可以告訴我們一些新的事情。有時候會有這樣的經(jīng)歷,在我們寫任何實現(xiàn)代碼之前測試可以正常通過。一般情況下,這樣的事情不應該發(fā)生,TDD教導我們編寫不能通過的測試。因為我們是先寫測試代碼,功能代碼還沒有開發(fā),這時測試代碼能跑通就說明存在問題。我們需要確定問題的來源,是不是運行環(huán)境已經(jīng)提供了該方法的實現(xiàn),或者我們有沒有必要保留這條測試用例。
?
步驟3:確保測試通過
準備好運行失敗的測試代碼之后,我們要做的就是編寫實現(xiàn)代碼,并保證測試代碼可以運行通過。有時候我們甚至需要硬編碼,不必擔心在這個步驟中我們的代碼是多么糟糕,在后面的重構環(huán)節(jié)我們可以優(yōu)化他。編寫實現(xiàn)代碼的時候,我們要尋找最明顯的簡潔實現(xiàn)方式,如果沒有我們可以偽造他,而把具體的實現(xiàn)拖延到后面。
1.你不需要他
在極限編程中,TDD的精髓是“你不需要他”,意思是直到需要的時候才需要添加相關功能。基于假設添加一些以后可能會用到的代碼,會讓我們的代碼變得很膨脹。對于動態(tài)語言,特別是javascript,違反這一原則有時候?qū)ξ覀兪怯姓T惑的,他可以增加代碼的靈活性。一個例子是,為函數(shù)添加過多的參數(shù)。除非有那樣的需求,否則不要這樣做。
2.通過String.prototype.trim測試
下面的代碼是為滿足之前的測試開發(fā)的。
String.prototype.trim = function () {return this.replace(/^\s+/, ""); };可以看到,這個實現(xiàn)還不完善,只去除了左空格。但是TDD就是這樣的,每一步都很小,只要能讓測試通過就可。發(fā)現(xiàn)新的需求點后,編寫測試代碼,然后完善實現(xiàn)代碼并通過測試。
3.能夠工作的最簡單的方案
最簡單的解決方案有時候意味著,可能要往產(chǎn)品中添加硬編碼的代碼。因為有時候一般的解決方案可能不是那么明顯,我們可以用硬編碼的方式推進我們的項目,等到有了解決方案的時候再替換。雖然硬編碼可以推進我們的項目,代碼質(zhì)量是我們的最終目標。
步驟4:移除重復的重構
最后,最重要也是最有趣的工作就是使代碼變得整潔。當實現(xiàn)代碼開發(fā)完畢,測試順利跑通之后,我們就可以考慮重構的工作了,把一些重復的代碼移除。這期間只有一條準則:測試必須能跑通。關于重構好的建議是,每次只對一個操作進行修改,并保證測試能夠通過。重構是對已有代碼的維護修改,所以測試不能失敗。
重復的代碼可能會出現(xiàn)在不同位置,有時候他是為了解決硬編碼的解決方案。如果我們有一個硬編碼的假冒響應,我們需要給他添加另外的測試,讓他在不同輸入的條件下失敗。或許我們一時還想不到替換硬編碼的方案,但至少我們知道問題的存在,他為我們提供了足夠的信息,方便我們找到最終解決方案。
重復的代碼同樣可能存在于測試代碼中,例如setup中的請求對象和假冒的依賴。測試代碼也是代碼,同樣需要維護,移除重復內(nèi)容。如果測試代碼和系統(tǒng)過于耦合,我們需要抽取幫助方法和對結構進行重構。setup和teardown可以用來集中設置對象的創(chuàng)建和銷毀。
我們在重構的過程中不能讓測試失敗。如果在重構的過程中我們沒有用更少的代碼完成工作,我們就需要考慮把工作托到以后再做。
步驟5:重復工作
一旦所有工作都完成了,沒有重復代碼了,也沒有重構工作需要做了,這時候就從todo列表中找一個新任務,重復上面的步驟。根據(jù)需要重復這樣的工作。你熟悉了這一過程之后,可以放大腳步,但是確保你的周期很短,這樣可以得到及時的反饋。
功能滿足需求之后,我們可以考慮提高測試的覆蓋率,可以添加對邊界值的測試、對依賴項的測試、不同輸入類型的測試、不同組件之間的整合測試等。下面是我們?yōu)镾tring.prototype.trim添加的第二條測試:
"test trim should remove trailing white-space": function () {assert("should remove trailing white-space", "a string" === "a string ".trim()); }總結
以上是生活随笔為你收集整理的测试驱动javascript开发 -- 4.测试驱动开发过程(下)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 11-04作业
- 下一篇: JavaScript中为何要使用prot