[javascript] view plaincopy1. if (diff > measure) {
2. num = Math.floor(diff / measure);
3. diff = diff - (num * measure); // BUG: This was missing in our first attempt
4. pieces.push(format(num, consider[i]));
5. }
一旦我們定位并且修正該故障,我們清除所有的console.log調用,避免代碼在沒有定義console對象的瀏覽器中崩潰。
分步調試器
Firebug和類似的工具使調試javascript比過去更容易。但是很多人似乎認為console.log是比原始的alert更高級的工具。的確,console不阻塞UI并且更少可能讓你強制關閉瀏覽器,但是console.log調試和alert調試是一樣的優雅或不優雅。
一個稍微復雜的方法是使用像Firebug一樣的工具使分步調試成為可能。
使用分步調試,你可以通過設置一些斷點和檢查所有有效值而不是記錄每個你想查看的變量的值來節省一些時間。
Console.log的問題
Console.log風格調試有一些問題:首先,console.log有討厭的引入自身缺陷的風險。如果在演示或部署之前忘記移除最后記錄語句,你知道我在說什么。懸浮的記錄語句會使你的代碼在不支持console對象的瀏覽器上崩潰,包括Firebug不可用時的火狐。“但是JavaScript是動態的”,我聽到你說,“你可以定義你自己的無操作的console,然后問題就會消除”。的確,你可以這樣做,但那就像是用刷漆解決你的汽車生銹的問題。
如果懸浮的console.log調用是不可接受的,我們立即認識到下一個問題:它是不可重復的。一旦調試會話結束,你去除了所有的記錄語句。如果(當)新問題出現在代碼的相同部分時,你又回到了起點,重新采用巧妙的記錄語句。分步調試也同樣是暫時的。特設調試(Adhoc debugging)是費時的、容易出錯的和不可重復的。
更有效的發現缺陷
單元測試是查找缺陷和驗證正確性的方法,并且不需要面對調試器臨時性和人為console.log/alert調試。單元測試還有其他大量的優勢,我將通過這篇文章介紹。
什么是單元測試
單元測試是你的產品代碼按照預期結果的可執行部分。例如,假如我們之前在 jQuery.fn.differenceInWords中發現有兩個錯誤沒有修正,并試圖用單元測試找到它們:
[javascript] view plaincopy1. var second = 1000;
2. var minute = 60 * second;
3. var hour = 60 * minute;
4. var day = 24 * hour;
5.
6. try {
7. // Test that 8 day difference results in "1 week ago"
8. var dateStr = new Date(new Date() - 8 * day).toString();
9. var element = jQuery('Replace me');
10. element.differenceInWords();
11.
12. if (element.text() != "1 week ago") {
13. throw new Error("8 day difference expected\n'1 week ago' got\n'"+
14. element.text() + "'");
15. }
16.
17. // Test a shorter date
18. var diff = 3 * day + 2 * hour + 16 * minute + 10 * second;
19. dateStr = new Date(new Date() - diff).toString();
20. var element = jQuery('Replace me');
21. element.differenceInWords();
22.
23. if (element.text() != "3 days, 2 hours, 16 minutes and 10 seconds ago") {
24. throw new Error("Small date difference expected\n" +
25. "'3 days, 2 hours, 16 minutes and 10 seconds ago' " +
26. "got\n'" + element.text() + "'");
27. }
28.
29. alert("All tests OK!");
30. } catch (e) {
31. alert("Assertion failed: " + e.message);
32. }
上面的測試用例處理具有已知具有時間屬性的元素,并在得到的人性化的結果字符串不是我們期望的結果時拋出異常。該代碼可以保存到獨立的文件或在加載該插件的頁面中包含。在一個瀏覽器中運行會立即讓我得到“所有測試正常”或一個指示什么錯了的消息。
用這種方法調試你的代碼好像很笨拙。我們不僅要寫記錄語句來幫助我們監測代碼,而且我們還不得不用程序創建元素和通過插件運行它們來驗證產生的文本。但這種方法有相當多的好處:
該測試可以在任何時間,任何瀏覽器上重復運行。
無論什么時間當我們改變代碼,我們都要記得運行該測試,它可以極大的保證同樣的缺陷不會重新回來。
適當的清理,這些測試提供了代碼的文檔。
測試是自我檢查的。無論我們添加了多少測試,我們仍然只有一個頁面來驗證是否有錯誤。
測試和產品代碼沒有沖突,因此不會在作為產品代碼的部分發布時帶入內部alert和console.log調用的風險。
寫該測試帶來稍多的初始化效果,但我們只寫一次,我們很快的會在下次需要調試同樣的代碼時節省時間。
使用單元測試框架
剛才我們寫的測試包含相當多的套路。幸運的是,已經有很多的測試框架來幫助我們。使用測試框架讓我們減少不得不嵌入到測試中的測試邏輯的數量,它進而也減少測試自身的缺陷??蚣芤部梢越o我們更多的自動測試和顯示結果的選項。