編寫可測試的Javascript程式碼(2):從反模式進行重構

abell123發表於2014-05-29

這是介紹“如何編寫可測試的Javascript UI程式碼”兩篇文章中的第二篇。

在第一篇文章反模式及其它們的解決方案中用一個示例應用程式引入了幾個常見的,可避免的,約束了可測試性的反模式,並且解釋了為什麼這些常見的做法是反模式的以及如何修復他們。

這篇文章來繼續重構原來的應用,以使得它的程式碼更容易閱讀,更容易被複用以及更容易進行測試。一旦重構完成,測試就要開始:建立測試工具,開發XHR模擬,最後,新增一個完整的單元測試用例。

使用最佳實踐來編寫可測試的UI程式碼

在第一篇文章“反模式及其解決方案”中,列出了幾個使得UI程式碼可測試的最佳實踐:
1.外鏈所有的Javascript;
2.提供公共介面;
3.使用可例項化的物件;
4.減少巢狀回撥;
5.將DOM事件處理程式與事件處理函式相分離;
6.當非同步函式完成的時候通知訂閱者;
7.測試完成後清除沒用的物件;
8.在XHR請求中新增模擬物件;
9.把應用初始化分離成自己的模組。
用這個清單作為指引,原來的應用程式被會完全重構,並能達到我們對程式碼進行單元測試的目的。

從HTML開始–外鏈所有的指令碼

原來內聯在HTML檔案中的Javascript程式碼已經被放在了外部並且被放置於兩個檔案中:authentication-form.js 和start.js。原來的大部分的邏輯放於authentication-form.js模組中,應用的初始化則在start.js中進行。

引用自index.html

擁有可選的公共介面的邏輯封裝模組

AuthenticationForm是一個公共的可獲取的模組,該模組比較簡潔地封裝了大部分的原始邏輯。AuthenticationForm提供了一個公共的介面,通過該介面其功能可以被測試。

一個公共介面–引用自authentication-form.js

使用可初始化的物件

原來的form表單例子沒有可例項化的部分,這也就意味著它的程式碼只能執行一次。這樣的話有效的單元測試幾乎是不可能的。重構了的AuthenticationForm是一個原型物件,使用Object.create來建立新的例項。

可例項化的物件-引用自authentication-form.js

減少巢狀回撥的使用

重構的AuthenticationForm從原來的的深層巢狀回撥(導致程式碼成金字塔狀)抽取邏輯形成四個公共的可以獲取得到的函式。這些函式中的兩個被用來提供給物件初始化和銷燬,其餘的兩個用於測試介面。

去除金字塔–引用自authentication-form.js

將DOM事件處理程式從事件行為中分離出來

將DOM事件處理程式從事件行為中分離出來有助於程式碼的重用和可測試。

將DOM事件處理程式從事件行為中分離出來–引用自authentication-form.js

在非同步函式中使用回撥(或者其他的通知機制)

AuthenticationForm的測試介面中的兩個函式,submitForm和checkAuthentication是非同步的。當所有處理程式都完成的時候他們都接受一個函式進行回撥。

有回撥函式的非同步回撥–引用自authentication-form.js

將沒用的物件處理掉

單元測試應該獨立的進行。任何的狀態,包括附屬的DOM事件處理器,在測試的時候必須被重置。

移除附加的DOM事件處理程式–引用自authentication-form.js

將應用的初始化邏輯分離出一個單獨的(初始化)模組

start.js是一個自呼叫的函式,它在多有的js檔案都下載完成後執行。因為我們的應用很簡單,只需要很少的初始化程式碼–一個AuthenticationForm例項被建立並初始化。

start.js

在這一點上,原來的整個應用程式被重構並且重新實現了。使用者應該能看到在功能上並沒有做改變,純粹是程式碼結構的修改。

如何進行單元測試?

儘管當前我們的程式碼是可測試的,一篇關於單元測試的文章卻沒有寫任何相關的測試程式碼!有幾個高質量的測試框架,在這個例子中我們使用QUnit。

首先,我們需要一個測試工具。一個測試工具由一個模擬DOM和Javascript程式碼組成。模擬DOM由測試中要使用的元素組成,通常是類似於form或者你要檢測可見性的元素這些東西。為了避免測試交叉汙染,在每一個單元測試之後都將DOM元素進行重置。QUnit期望模擬元素包含在id為#qunit-fixture的元素中。

Javascript程式碼包含一個單元測試執行器,要被測試的程式碼,獨立的模擬以及對他們自己的一些測試。

測試工具–引用自tests/index.html

書寫XHR模擬

XHR請求需要依賴於服務端,從前端發起的請求必須被服務端響應否則應用什麼都幹不了。用真正的XHR請求進行測試意味著服務端必須做好準備,這會嚴重阻礙前後端並行開發。

與其發起真正的XHR請求,不如使用一個模擬的請求來做。模擬物件是一些替代物件–可以在測試中進行精確地控制。一個模擬物件必須實現使用者要使用的所有功能。幸運的是,XHR模擬(也叫AjaxMock)只需要實現所有jQuery.ajax功能的很小的一部分即可。這個單獨的模擬功能提供了整合所有服務端響應的能力。幾個額外的函式被加進來輔助單元測試。

AjaxMock介面

完成一些測試!

現在,測試工具和XHR模擬都已經準備好了,我們可以寫一些單元測試了!測試包含6個獨立的測試。每個測試都會例項化一個新的AuthenticationForm物件和XHR模擬。XHR模擬可以為每一個可能的後端響應編寫測試。

總結

花了一些時間,但是我們達到了我們的目的。我們的程式碼易於閱讀,易於重用,並且有一個完整的測試套件。

編寫可測試的程式碼通常是一個挑戰,但是你一旦適應,基礎的部分還是很容易的。在開始一行程式碼之前,你要問你自己“我要如何來對程式碼進行測試?”。這個簡單的問題最終將節省大量時間和並在你重構或新增新功能的時候給你信心。

所有的程式碼在Github上可以獲取:https://github.com/shane-tomlinson/shanetomlinson.com/tree/master/2013-jan-writing-testable-ui-javascript

如果你有任何問題,歡迎發表評論。

最終產品

index.html

authentication-form.js

start.js

tests/index.html

tests/ajax-mock.js

tests/authentication-form.js

打賞支援我翻譯更多好文章,謝謝!

打賞譯者

打賞支援我翻譯更多好文章,謝謝!

任選一種支付方式

編寫可測試的Javascript程式碼(2):從反模式進行重構 編寫可測試的Javascript程式碼(2):從反模式進行重構

相關文章