或許在用任何程式設計語言進行開發時最耗時的一個環節都是測試和調試代碼。對於專業級的代碼來說,確保你所編寫的東西是經過完全測試的、可驗證和零bug的,變得極度重要。使得JavaScript與其它程式設計語言如此不同的一個方面是,它並不被任何單獨的公司或組織單獨擁有和支援(不像C#,PHP,Perl,Python,或者Java)。這一差異使得擁有一個調試和測試代碼的一致的基礎變得極具挑戰性。
為了削減你可能必須忍受的工作量和壓力,捕獲JavaScript錯誤時,有許多強大的開發工具可供使用。每一種現代瀏覽器都有各自的(品質參差的)工具。使用它們會使得JavaScript開發呈現加一致的局面和更加遠大的前景。
本章中我討論用於調試JavaScript代碼的不同的工具,然後建立可靠的、可重用的用以檢驗未來開發的測試套件。
調試
測試和調試是不可分離的兩個環節。當你在為你的代碼建立廣泛的測試案例的時候,你肯定將會碰到一些奇怪的需要注意的錯誤。這正是調試環節發揮功用的地方。瞭解怎樣使用可用的最好的工具來找到並修複你程式中的缺陷,可以使你的代碼有保障且更快地運行。
錯誤控制台
大多數現代瀏覽器裡都可用的最易使用的工具是某種形式的錯誤控制台。控制台的品質、介面的可訪問性以及錯誤訊息的品質,在不同的瀏覽器之間都有很大差異。最終,你可能會現最好使用某種單獨的擁有最適合開發人員的錯誤控制台(或其它用於調試的外掛程式)的瀏覽器來開始你的調試過程。
Internet
Explorer
擁有最受歡迎的瀏覽器並不意味著就擁有了最好的調試工具。不幸的是,IE的錯誤控制台有著嚴重的不足。問題之一就是,控制台預設是關閉的,如果你不把IE用作預設瀏覽器(十分懷疑任何有自尊心的JavaScript開發人員會這麼幹),這一點會使得錯誤的搜尋變得更加令人困惑。
除了上面所提到的可用性,IE的錯誤控制台的最麻煩的問題是以下幾點:
a.
每次只顯示一個錯誤;你必須依賴菜單系統來找到其它錯誤。
b.
錯誤訊息含義相當模糊,幾乎沒有邏輯意義。它們很少給出發生的問題的精確描述。
c.
報告的錯誤總是位移一行,也就是說實際出錯的行號總是比所報告的小一。這一點與含糊的錯誤結合起來,你可能會飽受bug之苦。
圖4-1是發生錯誤時的IE錯誤控制台的例子。
4-1.gif (7.01 KB) 2007-4-12 11:04
圖4-1.IE中的JavaScript錯誤控制台
正如我在本節開頭所提到的,在另一個(非IE)瀏覽器中開始你的JavaScript調試過程或許是一個非常好的主意。一旦你在那種瀏覽器中徹底清除了所有bug,你應該可以更容易地定位出現在IE裡的錯綜複雜的問題。
Firefox
過去的幾年裡Firefox瀏覽器在UI方面取得了極大的發展,極大地協助著web開發人員更加輕鬆地開發更好的網站。JavaScript錯誤控制枱曆經了多次更新,產生了一些很有用的東西。
a.
控制台允許你輸入任意的JavaScript命令。這一點用於斷定頁面載入以後變數是什麼值是極其有用的。
b.
控制台提供了依據其類型(如錯誤、警告或者訊息)將訊息分類的能力。
c.
最新版的控制台連同JavaScript錯誤一起提供了樣式表警告。儘管在設計拙劣的網站上它可能會提供大量的不必要的錯誤訊息,但是通常在找出你自己的布局缺陷時它還是很有協助的。
d.
該錯誤控制台的一個缺點是它並不根據你正在瀏覽哪一個頁面來過濾訊息,這就是說你會得到不同頁面的錯誤的混合。(下節我要講到的Firebug外掛程式,解決了這一問題。)
圖4-2所示是Firefox錯誤控制台一個截屏。注意你可以使用不同的按鈕在不同類型的錯誤訊息之間切換。
4-2.gif (22.49 KB) 2007-4-12 11:04
圖4-2.
Firefox的JavaScript錯誤控制台
雖說Firefox的錯誤控制台非常好,但它並不是完美的。正是因為如此,開發人員們趨向於求助於各種Firefox外掛程式來更好地調試他們的應用程式。稍後我將討論這些外掛程式。
Safari
Safari瀏覽器是市場上最新的瀏覽器之一,也是成長相當快的一個。這一成長導致其JavaScript支援(無論是開發還是執行時)都有時很不穩定。出於此,在該瀏覽器裡JavaScript控制台不易進入。它甚至不是一個容易啟用的選項,完全隱藏在一個一般使用者不可訪問的秘密的調試菜單裡。
為了啟用調試菜單(JavaScript控制台)你需要(在Safari沒有運行時)在一個終端上執行如1-4所示的命令。
程式4-1.
讓Safari顯示調試菜單的的命令
複製內容到剪貼簿
代碼:
defaults write com.apple.Safari IncludeDebugMenu
1
下一次開啟Safari時,你就有了一個新的包含JavaScript控制台選項的調試菜單。
如同你可以從其隱密的位置推測的那樣,這個控制台還處於一個很原始的狀態。關於Firefox錯誤控制台需提及的有以下幾點:
a.
錯誤訊息通常十分含糊,大致跟IE的錯誤訊息在同一個層級上。
b. 提供了錯誤所在的行號,但是經常被重設為零,把你扔回你開始的地方。
c.
沒有根據頁面將訊息過濾,不過所有的訊息都將拋出該錯誤的指令碼列於其後。
圖4-3展示了運行於Safari2.0上的錯誤控制台的一個截屏。
4-3.gif (14.31 KB) 2007-4-12 11:04
圖4-3.
Safari的JavaScript錯誤控制台
作為一個web開發平台,Safari仍然落後得很遠。但是,WebKit開發組(開發了Safari渲染引擎的團隊)正致力於把該瀏覽器推向前沿並取得了良好的進展。期待未來的幾個月和幾年裡該瀏覽器的會有很多新發展。
Opera
最後我們來看看Opera瀏覽器包含的錯誤控制台。值得感激的是,Opera投入了大量的時間和精力,使得它功能豐富且非常有用。除了Firefox控制台所具有的所有特性之外,它還提供了以下幾點:
a.
描述性的錯誤訊息,給你對問題的良好理解
b. 內聯的程式碼片段,用代碼本身來說明錯誤出在哪裡
c.
錯誤可以根據類型(如,Javascript,CSS,等等)過濾
遺憾的是,這個錯誤控制台沒有執行JavaScript命令的能力(譯註:這句及以上幾句著實讓人鬱悶,明明說了除firefox所具有的還怎麼地怎麼地,列舉的三條裡倒有兩條是firefox已經有的,到現在又搞出個firefox有而它沒有的了),而那是如此有用的一個功能。儘管如此,這些加起來,已經構成一個很好的錯誤控制台了。圖4-4展示了Opera9.0中錯誤控制台的一個截屏。
4-4.gif (23.82 KB) 2007-4-12
11:04
圖4-4.Opera的JavaScript錯誤控制台
Opera長期以來一直認真地對待web開發。開發隊伍擁有眾多主動而幹勁十足的開發人員和文檔編寫者,Opera平台一直在為更好地服務於web開發人員而奮鬥。
DOM查看器
DOM查看是對JavaScript開發人員最有用的而沒有被充分利用的工具之一。DOM查看可以看成是作頁面原始碼查看的一個進階版本,允許你看到經過你的代碼修改其內容以後頁面的目前狀態。
每個瀏覽器裡不同的DOM查看器行為各異,有些提供了額外的功能,允許你深入地觀察你正操作的對象。這一節裡我將介紹三個不同的瀏覽器並討論是什麼使它們彼此有那麼大的差異。
Firefox的DOM查看器
Firefox的DOM查看器是一個預包含在所有Firefox安裝包裡的Firefox外掛程式(但預設是不安裝的)。這一外掛程式允許你在HTML文檔建立起來及被操作以後在其中導航。此外掛程式的一個截屏見圖4-5。
4-5.gif (94.11 KB) 2007-4-12 11:04
圖4-5.
Firefox的內建DOM查看器
在導航一個文檔的時候,你不僅能夠看到修改後的HTML元素的結構,還能看到每一個元素的style屬性以及它們的實體物件屬性。這能協助你確切地知道修改過以後網頁看起來是什麼樣子感覺怎麼樣。這使得查看器成為一個必不可少的工具。
Safari的web查看器
Safari有一個新的DOM查看器包含於其瀏覽器最近的版本裡。在某些方面它比Firefox的DOM查看器還要好,你可以右擊頁面的任何元素使查看器立即導航到它。圖4-6展示了(設計得非常雅緻的)Safari
DOM查看器的一個截屏。
4-6.gif (52.34 KB) 2007-4-12
11:04
Safari的內建DOM查看器
儘管這一外掛程式包含於Safari的最近版本裡,但啟用它甚至比前面提及的JavaScript控制台更麻煩。這事實在讓人摸不透:為什麼Safari團隊付出了那麼多的努力去編寫和加入這些組件,最後卻又對希望使用它們的開發人員隱藏起來。且不管這些,為了啟用那個DOM查看器,你必須執行如程式4-2所示的語句。
程式4-2.
啟用Safari的DOM查看器
複製內容到剪貼簿
代碼:
defaults write com.apple.Safari WebKitDeveloperExtras
-bool
true
Safari的DOM查看器仍有許多地方需要發展和改進,好在Safari的Team Dev是很有才能的。但是目前來說,你可能最好還是使用Firefox作為你的開發的基礎,直到Safari徹底完成和發布。
View
Rendered Source
最後,我要介紹Web開發人員可用的最易使用的DOM查看器。Firefox外掛程式View Rendered
Source在通常的查看看原始碼選項下面加入一個供選擇的功能表項目,以一種直觀的可理解的方式為你提供新的HTML文檔的完整表述。關於該外掛程式的更多資訊可在其網站上找到:http://jennifermadden.com/。
除了提供一個感覺非常自然的原始碼視圖以外,它還為文檔的每一層提供了分級的代碼著色,讓你更好的感覺到你到底位於代碼的哪一處。4-7所示。
4-7.gif (24.91 KB) 2007-4-12 11:04
圖4-7. Firefox的外掛程式View
Rendered Source
View Rendered
Source外掛程式應該是每一個web開發人員工具箱的標準工具。它的基本用途遠遠超越了原始的原始碼查看所給出的,同時仍允許向更複雜的Firefox
DOM查看器外掛程式的平滑升級。
Firebug
Joe
Hewitt創造的Firebug是近年來出現的最重要的JavaScript開發外掛程式之一。作為一個完整的JavaScript開發包,它有一個錯誤控制台,一個調試器,和一個DOM查看器。關於此外掛程式的更多資訊可以從它的網站上找到:http://www.joehewitt.com/software/firebug/。
將這麼多的工具結合起來的一個最主要的優點就是,你可以更好的推斷出問題出在哪裡。比如說,當點擊一條錯誤訊息的時候,JavaScript檔案名稱及錯誤發生的行號將會呈現給你。於是你可以設定斷點,介入代碼的執行,更好地把握問題出現的來龍去脈。此外掛程式的一個截屏見圖4-8。
4-8.gif (25.06 KB) 2007-4-12 11:04
圖4-8.
Firebug調試外掛程式
現代調試工具發展至今,還沒有出現比Firebug更好的。我強烈推薦你選擇Firefox作為你的基礎JavaScript開發平台,並聯合使用Firebug外掛程式。
Venkman
The last piece of the JavaScript development puzzle is the Venkman
extension(不知從何puzzle起)。最初作為Mozilla瀏覽器的一部分,Verkman是由Mozilla發起的JavaScript調試器項目的代碼名稱。關於此項目的更多資訊和更新了的Firefox外掛程式可以在以下網站找到:
Mozilla的Venkman項目:http://www.mozilla.org/projects/venkman/
Firefox的Venkman外掛程式:https://addons.mozilla.org/firefox/216/
Venkman教程:http://www.mozilla.org/projects/venkman/venkman-walkthrough.html
使用這樣一種外掛程式的重要性在於,由於與JavaScript引擎本身的深入結合,它能夠讓你對代碼到底在做些什麼進行更進階的控制。圖4-9是Firefox的Vernkman外掛程式的一個截屏。
4-9.gif (85.57 KB) 2007-4-12 11:04
圖4-9.
與Firefox介面的曆史悠久的JavaScript調試器Venkman
利用這一外掛程式引入的所有額外控制,除了能進入程式碼分析其執行過程以外,你還可以確切地知道在一個範圍裡什麼變數對你是可用的以及有關屬性或變數狀態的確切資訊。
測試 (因為我對代碼測試沒什麼瞭解,本節內容的翻譯十分勉強。請多包涵)
個人來說,我把測試的過程和測試案例的建立看作是"[color]future-proofing(沒想出合適的詞表達它)"你的代碼。為你的基礎代碼或庫建立可信賴的測試案例,可以為你省下無數用來調試代碼和尋找你在調試時不經意引入的怪異的bug的時間。
作為這是多數現代編程環境共有的慣例,擁有一套可靠的測試案例,不僅能協助你自己、同時也能協助其它使用你的程式碼程式庫的人添加新的功能並修複錯誤。
在這一節裡我將介紹能用來建立JavaScript測試套件的三種不同的庫,它們都能以跨瀏覽器的、自動化的方式執行。
JSUnit
JSUnit長期以來幾乎一直是JavaScript單元測試的黃金標準。它的大多數功能基於為Java設計的流行的JUnit包,這意味著如果你熟知JUnit是怎樣通過Java工作的,你將能輕易使用此庫。它的網站(http://www.jsunit.net/)上有大量的可用的資訊和文檔(http://www.jsunit.net/documentation/)。
跟大多數(至少是我這一節所討論的全部)測試套件一樣,它由三個基本組件組成:
測試回合器(Test
runner):套件的這一部分提供一個良好的圖形化輸出,顯示當前已經運行到全部操作的哪一個階段。它提供了載入測試組和並行其內容的能力,記錄它們提供的所有輸出。
測試組(Test
suite):這是所有測試案例(有時分佈於多個網頁)的集合。
測試案例(Test
cases):這是一些獨立的可以求值到true/false運算式的命令,給你可計量的結果來判定你的代碼是否正確地工作。單獨的一個測試案例可能不是太有用,但是當配合測試回合器一起使用的時候,你將會得到有用的互動體驗。
所有這些加起來,構成了全面的自動的測試套件,可用來運行和加入將來的測試。程式4-3展示了一個簡單的測試組,程式4-4則是套測試案例。
代碼4-3.
使用JSUnit建立的測試組
複製內容到剪貼簿
代碼:
<html>
<head>
<title>JsUnit Test
Suite</title>
<script
src="../app/jsUnitCore.js"></script>
<script>
function
suite() {
var newsuite = new
top.jsUnitTestSuite();
newsuite.addTestPage("jsUnitTests.html");
return
newsuite;
}
</script>
</head>
<body></body>
</html>
程式4-4.
JSUnit裡可用於典型的測試頁的各種測試案例
複製內容到剪貼簿
代碼:
<html>
<head>
<title>JsUnit
Assertion Tests</title>
<script
src="../app/jsUnitCore.js"></script>
<script>
//測試一個運算式為真
function
testAssertTrue() {
assertTrue("true should be true",
true);
assertTrue(true);
}
//測試一個運算式為假
function
testAssertFalse() {
assertFalse("false should be false",
false);
assertFalse(false);
}
//檢查兩個參數是否相等的測試
function
testAssertEquals() {
assertEquals("1 should equal 1", 1,
1);
assertEquals(1, 1);
}
//檢查兩個參數是否不相等的測試
function
testAssertNotEquals() {
assertNotEquals("1 should not equal 2", 1,
2);
assertNotEquals(1, 2);
}
//檢查參數是否為null的測試
function
testAssertNull() {
assertNull("null should be null",
null);
assertNull(null);
}
//檢查參數不為null的測試
function
testAssertNotNull() {
assertNotNull("1 should not be null",
1);
assertNotNull(1);
}
</script>
</head>
<body></body>
</html>
為JSUnit的編寫的文檔非常好,而且因為它已經出現很長一段時間了,你很有希望有找到很好的實際應用的例子。
J3Unit
J3Unit是JavaScript單元測試領域的新兵。這一特別的庫所提供的超越於JSUnit的功能在於,它能直接與伺服器端的測試套件(如JUnit或Jetty)溶合。對JavaScript開發人員來說,這可能是極其有用的,因為他們能夠同時為他們的用戶端和伺服器端代碼快速地遍曆所有的測試案例。然而,由於不是所有的人都使用Java,J3Unit也提供了一個靜態模式,可以像其它的單元測試器一樣執行於你的瀏覽器中。關於J3Unit的更多資訊可以在它的網站上找到:http://j3unit.sourceforge.net]/。
因為將伺服器端代碼與客戶器端的測試案例掛勾是很罕見的特例,我們來看看J3Unit的靜態用戶端測試單元是如何工作的。可喜的是,它們實際是跟其它的測試套件沒有什麼不同,這使得移植非常簡單。如程式4-5的代碼所示。
程式4-5.
使用J3Unit執行的簡單測試
複製內容到剪貼簿
代碼:
<html>
<head>
<title>Sample
Test</title>
<script src="js/unittest.js"
type="text/javascript"></script>
<script src="js/suiterunner.js"
type="text/javascript"></script>
</head>
<body>
<p
id="title">Sample Test</p>
<script
type="text/javascript">
new
Test.Unit.Runner({
//測試顯示和顯示元素
testToggle: function() {with(this)
{
var title =
document.getElementById("title");
title.style.display =
'none';
assertNotVisible(title, "title should be
invisible");
element.style.display =
'block';
assertVisible(title, "title should be
visible");
}},
//測試將一個元素添加到另一個裡
testAppend:
function() {with(this) {
var title =
document.getElementById("title");
var p =
document.createElement("p");
title.appendChild( p
);
assertNotNull( title.lastChild );
assertEqual(
title.lastChild, p
);
}}
});
</script>
</body>
</html>
JSUnit,儘管還非常新,卻已經展現了單元測試架構的光明前景。如果你對它的面對對象風格感興趣,我推薦你試試。
Test.Simple
JavaScript單元測試的最後一個例子是另一個新來者。Test.Simple由JSAN的建立所引入,作為一種方式來標準化所有提交的JavaScript模組的測試。因為它的廣泛應用,Test.Simple擁用大量的文檔和使用執行個體,這是使用一個測試架構時非常重要的兩個方面。關於Test.Simple(及其姊妹庫Test.More)的更多資訊可以在這裡找到:
Test.Simple:
http://openjsan.org/doc/t/th/theory/Test/Simple/
Test.Simple文檔:
http://openjsan.org/doc/t/th/theory/Test/Simple/0.21/lib/Test/Simple.html
Test.More文檔:
http://openjsan.org/doc/t/th/theory/Test/Simple/0.21/lib/Test/More.html
Test.Simple庫提供了大量的用來調試的方法,以及一個完整的測試回合器,提供自動化的測試執行。程式4-6是一個Test.Simple測試組的樣本。
程式4-6.
使用Test.Simple和Test.More執行測試
複製內容到剪貼簿
代碼:
//載入Test.More模組(用來測試它自身)
new
JSAN('../lib').use('Test.More');
//計劃發生6個測試(以便出問題的時候知道)
plan({tests:
6});
//測試3個簡單的案例
ok( 2 == 2, 'two is two is two is two' );
is( "foo",
"foo", 'foo is foo' );
isnt( "foo", "bar", 'foo isnt
bar');
//使用正則表達的測試
like("fooble", /^foo/, 'foo is like
fooble');
like("FooBle", /foo/i, 'foo is like
FooBle');
like("/usr/local/", '^\/usr\/local', 'regexes with slashes in like'
);
個人而言,我比較欣賞Test.Simple和Test.More的簡單性,因為它們不會太多常規開銷,有助於保持你的代碼簡潔。當然歸根結底,決定使用哪種最適合你的測試套件還得看你自己,為你的代碼選擇測試套件是太過重要的而不可忽略的一個話題。