为了削减你可能必须忍受的工作量和压力,捕获JavaScript错误时,有许多强大的开发工具可供使用。每一种现代浏览器都有各自的(质量参差的)工具。使用它们会使得JavaScript开发呈现加一致的局面和更加远大的前景。
本章中我讨论用于调试JavaScript代码的不同的工具,然后建立可靠的、可重用的用以检验未来开发的测试套件。
调试
测试和调试是不可分离的两个环节。当你在为你的代码建立广泛的测试用例的时候,你肯定将会碰到一些奇怪的需要注意的错误。这正是调试环节发挥功用的地方。了解怎样使用可用的最好的工具来找到并修复你程序中的缺陷,可以使你的代码有保障且更快地运行。
错误控制台
大多数现代浏览器里都可用的最易使用的工具是某种形式的错误控制台。控制台的质量、界面的可访问性以及错误消息的质量,在不同的浏览器之间都有很大差异。最终,你可能会现最好使用某种单独的拥有最适合开发者的错误控制台(或其它用于调试的插件)的浏览器来开始你的调试过程。
Internet
Explorer
拥有最受欢迎的浏览器并不意味着就拥有了最好的调试工具。不幸的是,IE的错误控制台有着严重的不足。问题之一就是,控制台默认是关闭的,如果你不把IE用作缺省浏览器(十分怀疑任何有自尊心的JavaScript开发者会这么干),这一点会使得错误的搜寻变得更加令人困惑。
除了上面所提到的可用性,IE的错误控制台的最麻烦的问题是以下几点:
a.
每次只显示一个错误;你必须依赖菜单系统来找到其它错误。
b.
错误消息含义相当模糊,几乎没有逻辑意义。它们很少给出发生的问题的精确描述。
c.
报告的错误总是偏移一行,也就是说实际出错的行号总是比所报告的小一。这一点与含糊的错误结合起来,你可能会饱受bug之苦。
图4-1是发生错误时的IE错误控制台的例子。
图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. Firefox的JavaScript错误控制台
虽说Firefox的错误控制台非常好,但它并不是完美的。正是因为如此,开发者们趋向于求助于各种Firefox插件来更好地调试他们的应用程序。稍后我将讨论这些插件。
Safari
Safari浏览器是市场上最新的浏览器之一,也是成长相当快的一个。这一成长导致其JavaScript支持(无论是开发还是执行时)都有时很不稳定。出于此,在该浏览器里JavaScript控制台不易进入。它甚至不是一个容易激活的选项,完全隐藏在一个一般用户不可访问的秘密的调试菜单里。
为了激活调试菜单(JavaScript控制台)你需要(在Safari没有运行时)在一个终端上执行如1-4所示的命令。
程序4-1. 让Safari显示调试菜单的的命令
代码:
defaults write com.apple.Safari IncludeDebugMenu
1
如同你可以从其隐密的位置推测的那样,这个控制台还处于一个很原始的状态。关于Firefox错误控制台需提及的有以下几点:
a. 错误消息通常十分含糊,大致跟IE的错误消息在同一个级别上。
b. 提供了错误所在的行号,但是经常被重置为零,把你扔回你开始的地方。
c. 没有根据页面将消息过滤,不过所有的消息都将抛出该错误的脚本列于其后。
图4-3展示了运行于Safari2.0上的错误控制台的一个截屏。
图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.Opera的JavaScript错误控制台
Opera长期以来一直认真地对待web开发。开发队伍拥有众多主动而干劲十足的开发者和文档编写者,Opera平台一直在为更好地服务于web开发者而奋斗。
DOM查看器
DOM查看是对JavaScript开发者最有用的而没有被充分利用的工具之一。DOM查看可以看成是作页面源代码查看的一个高级版本,允许你看到经过你的代码修改其内容以后页面的当前状态。
每个浏览器里不同的DOM查看器行为各异,有些提供了额外的功能,允许你深入地观察你正操作的对象。这一节里我将介绍三个不同的浏览器并讨论是什么使它们彼此有那么大的差异。
Firefox的DOM查看器
Firefox的DOM查看器是一个预包含在所有Firefox安装包里的Firefox插件(但默认是不安装的)。这一插件允许你在HTML文档建立起来及被操作以后在其中导航。此插件的一个截屏见图4-5。
图4-5. Firefox的内建DOM查看器
在导航一个文档的时候,你不仅能够看到修改后的HTML元素的结构,还能看到每一个元素的style属性以及它们的实体对象属性。这能帮助你确切地知道修改过以后网页看起来是什么样子感觉怎么样。这使得查看器成为一个必不可少的工具。
Safari的web查看器
Safari有一个新的DOM查看器包含于其浏览器最近的版本里。在某些方面它比Firefox的DOM查看器还要好,你可以右击页面的任何元素使查看器立即导航到它。图4-6展示了(设计得非常雅致的)Safari DOM查看器的一个截屏。
Safari的内建DOM查看器
尽管这一插件包含于Safari的最近版本里,但激活它甚至比前面提及的JavaScript控制台更麻烦。这事实在让人摸不透:为什么Safari团队付出了那么多的努力去编写和加入这些部件,最后却又对希望使用它们的开发者隐藏起来。且不管这些,为了激活那个DOM查看器,你必须执行如程序4-2所示的语句。
程序4-2. 激活Safari的DOM查看器
代码:
defaults write com.apple.Safari WebKitDeveloperExtras
-bool
true
View Rendered Source
最后,我要介绍Web开发者可用的最易使用的DOM查看器。Firefox插件View Rendered Source在通常的查看看源代码选项下面加入一个供选择的菜单项,以一种直观的可理解的方式为你提供新的HTML文档的完整表述。关于该插件的更多信息可在其网站上找到:http://jennifermadden.com/。
除了提供一个感觉非常自然的源代码视图以外,它还为文档的每一层提供了分级的代码着色,让你更好的感觉到你到底位于代码的哪一处。如图4-7所示。
图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. 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. 与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>
代码:
<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>
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>
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'
);