JsTestDriver library(JavaScript 单元测试框架)

一个损坏的JavaScript代码示例

Web应用程序面临的一个最大挑战是支持不同版本的Web浏览器。能在Safari上运行的JavaScript代码不一定能在Windows®InternetExplorer(IE)、Firefox或GoogleChrome上运行。这个挑战的根源是呈现层中的JavaScript代码从一开始就没有进行测试。如果没有对代码进行单元测试,那么在升级或支持新浏览器后,组织可能需要花钱反复测试Web应用程序。本文将展示如何通过高效的JavaScript代码单元测试降低测试成本。

一个常见用例是登录表单JavaScript验证。考虑清单1中的表单。

清单1.登录表单

<FORM>

<table>

<tr>

<td>Username</td>

<td><inputtype="text"id="username"/></td>

<td><spanid="usernameMessage"></span></td>

</tr>

<tr>

<td>Password</td>

<td><inputtype="password"id="password"/></td>

<td><spanid="passwordMessage"></span></td>

</tr>

<tr>

<td><inputtype="button"onclick="newappnamespace.

ApplicationUtil().validateLoginForm()"value="Submit"/></td>

</tr>

</table>

</FORM>

这个表单很简单,仅包含用户名和密码字段。单击提交按钮时,将通过ApplicationUtil执行一个特定的表单验证。以下是负责验证HTML表单的JavaScript对象。清单2显示了ApplicationUtil对象的代码。

清单2.损坏的ApplicationUtil对象代码

appnamespace={};

appnamespace.ApplicationUtil=function(){};

appnamespace.ApplicationUtil.prototype.validateLoginForm=function(){

varerror=true;

document.getElementById("usernameMessage").innerText="";

document.getElementById("passwordMessage").innerText="";

if(!document.getElementById("username").value){

document.getElementById("usernameMessage").innerText=

"Thisfieldisrequired";

error=false;

}

if(!document.getElementById("password").value){

document.getElementById("passwordMessage").innerText=

"Thisfieldisrequired";

error=false;

}

returnerror;

};

在清单2中,ApplicationUtil对象提供一个简单验证:用户名和密码字段都已填充。如果某个字段为空,就会显示一条错误消息:Thisfieldisrequired。

上面的代码能够在InternetExplorer8和Safari5.1上工作,但无法在Firefox3.6上工作,原因是Firefox不支持innerText属性。通常,(上述代码和其他类似JavaScript代码中的)主要问题是不容易发现编写的JavaScript代码是不是跨浏览器兼容的。

这个问题的一个解决方案是进行自动化单元测试,检查代码是不是跨浏览器兼容。

回页首

JsTestDriver

JsTestDriverlibrary是最好的JavaScript单元测试框架之一,它为JavaScript代码提供了跨浏览器测试。图1展示了JsTestDriver的架构。

图1.JsTestDriver架构

JsTestDriver架构

jsTestDriver是开源项目

jsTestDriver是Apache2.0许可下的一个开源项目,托管在GoogleCode上,后者是一个类似于SourceForge的项目存储库。只要使用OpenSourceInitiative批准的许可,开发人员就能在这个存储库中创建和管理公共项目。

还有许多其他JavaScript单元测试工具,请参见下面的参考资料部分中的其他工具,比如DojoObjectiveHarness(DOH)。

捕获不同的浏览器之后,服务器会负责将JavaScript测试用例运行程序代码加载到浏览器中。可以通过命令行捕获浏览器,也可以通过将浏览器指向服务器URL来捕获浏览器。一旦捕获到浏览器,该浏览器就被称为从属浏览器。服务器可以加载JavaScript代码,在每个浏览器上执行测试用例,然后将结果返回给客户端。

客户端(命令行)需要以下两个主要项目:

JavaScript文件,即源文件和测试文件

配置文件,用于组织源文件和测试文件的加载

这个架构比较灵活,允许单个服务器从网络中的其他机器捕获任意数量的浏览器。例如,如果您的代码在Linux上运行但您想针对另一个Windows机器上的MicrosoftInternetExplorer运行您的测试用例,那么这个架构很有用。

要使用JsTestDriver库,请先下载最新版的JsTestDriver1.3.2。

回页首

编写单元测试代码

现在开始编写JavaScript测试用例。为简单起见,我将测试以下用例:

用户名和密码字段均为空。

用户名为空,密码不为空。

用户名不为空,密码为空。

清单3显示了表示TestCase对象的ApplicationUtilTest对象的部分代码。

清单3.ApplicationUtilTest对象代码的一部分

ApplicationUtilTest=TestCase("ApplicationUtilTest");

ApplicationUtilTest.prototype.setUp=function(){

/*:DOC+=<FORMaction=""><table><tr><td>Username</td><td>

<inputtype="text"id="username"/></td><td><spanid="usernameMessage">

</span></td></tr><tr><td>Password</td><td>

<inputtype="password"id="password"/></td><td><spanid="passwordMessage"

></span></td></tr></table></FORM>*/

};

ApplicationUtilTest.prototype.testValidateLoginFormBothEmpty=function(){

varapplicationUtil=newappnamespace.ApplicationUtil();

/*Simulateemptyusernameandpassword*/

document.getElementById("username").value="";

document.getElementById("password").value="";

applicationUtil.validateLoginForm();

assertEquals("Usernameisnotvalidatedcorrectly!","Thisfieldisrequired",

document.getElementById("usernameMessage").innerHTML);

assertEquals("Passwordisnotvalidatedcorrectly!","Thisfieldisrequired",

document.getElementById("passwordMessage").innerHTML);

};

ApplicationUtilTest对象通过JsTestDriverTestCase对象创建。如果您熟悉JUnit框架,那么您肯定熟悉setUp和testXXX方法。setUp方法用于初始化测试用例。对于本例,我使用该方法来声明一个HTML片段,该片段将用于其他测试用例方法。

DOC注释是一个JsTestDriver惯用语,可以用于轻松声明一个HTML片段。

在testValidateLoginFormBothEmpty方法中,创建了一个ApplicationUtil对象,并在测试用例方法中使用该对象。然后,代码通过检索用户名和密码的DOM元素并将它们的值设置为空值来模拟输入空用户名和密码。可以调用validateLoginForm方法来执行实际表单验证。最后,将调用assertEquals来确保usernameMessage和passwordMessagespan元素中的消息是正确的,即:Thisfieldisrequired。

在JsTestDriver中,可以使用以下构件:

fail("msg"):表明测试一定会失败,消息参数将显示为一条错误消息。

assertTrue("msg",actual):断定实际参数正确。否则,消息参数将显示为一条错误消息。

assertFalse("msg",actual):断定实际参数错误。否则,消息参数将显示为一条错误消息。

assertSame("msg",expected,actual):断定实际参数与预期参数相同。否则,消息参数将显示为一条错误消息。

assertNotSame("msg",expected,actual):断定实际参数与预期参数不相同。否则,消息参数将显示为一条错误消息。

assertNull("msg",actual):断定参数为空。否则,消息参数将显示为一条错误消息。

assertNotNull("msg",actual):断定实际参数不为空。否则,消息参数将显示为一条错误消息。

其他方法的代码包含其他测试用例。清单4显示了测试用例对象的完整代码。

清单4.ApplicationUtil对象完整代码

ApplicationUtilTest=TestCase("ApplicationUtilTest");

ApplicationUtilTest.prototype.setUp=function(){

/*:DOC+=<FORMaction=""><table><tr><td>Username</td><td>

<inputtype="text"id="username"/></td><td><spanid="usernameMessage">

</span></td></tr><tr><td>Password</td><td>

<inputtype="password"id="password"/></td><td><spanid="passwordMessage"

></span></td></tr></table></FORM>*/

};

ApplicationUtilTest.prototype.testValidateLoginFormBothEmpty=function(){

varapplicationUtil=newappnamespace.ApplicationUtil();

/*Simulateemptyusernameandpassword*/

document.getElementById("username").value="";

document.getElementById("password").value="";

applicationUtil.validateLoginForm();

assertEquals("Usernameisnotvalidatedcorrectly!","Thisfieldisrequired",

document.getElementById("usernameMessage").innerHTML);

assertEquals("Passwordisnotvalidatedcorrectly!","Thisfieldisrequired",

document.getElementById("passwordMessage").innerHTML);

};

ApplicationUtilTest.prototype.testValidateLoginFormWithEmptyUserName=function(){

varapplicationUtil=newappnamespace.ApplicationUtil();

/*Simulateemptyusernameandpassword*/

document.getElementById("username").value="";

document.getElementById("password").value="anyPassword";

applicationUtil.validateLoginForm();

assertEquals("Usernameisnotvalidatedcorrectly!",

"Thisfieldisrequired",document.getElementById("usernameMessage").innerHTML);

assertEquals("Passwordisnotvalidatedcorrectly!",

"",document.getElementById("passwordMessage").innerHTML);

};

ApplicationUtilTest.prototype.testValidateLoginFormWithEmptyPassword=function(){

varapplicationUtil=newappnamespace.ApplicationUtil();

document.getElementById("username").value="anyUserName";

document.getElementById("password").value="";

applicationUtil.validateLoginForm();

assertEquals("Usernameisnotvalidatedcorrectly!",

"",document.getElementById("usernameMessage").innerHTML);

assertEquals("Passwordisnotvalidatedcorrectly!",

"Thisfieldisrequired",document.getElementById("passwordMessage").

innerHTML);

};

回页首

配置用于测试的不同浏览器

测试JavaScript代码的一个推荐实践是将JavaScript源代码和测试代码放置在不同的文件夹中。对于图2中的示例,我将JavaScript源文件夹命名为"js-src",将JavaScript测试文件夹命名为"js-test",它们都位于"js"父文件夹下。

图2.JavaScript测试文件夹结构

JavaScript测试文件夹结构

组织好源和测试文件夹后,必须提供配置文件。默认情况下,JsTestDriver运行程序会寻找名为jsTestDriver.conf的配置文件。您可以从命令行更改配置文件名称。清单5显示了JsTestDriver配置文件的内容。

清单5.JsTestDriver配置文件内容

server:http://localhost:9876

load:

-js-src/*.js

-js-test/*.js

配置文件采用YAML格式。server指令指定测试服务器的地址,load指令指出了将哪些JavaScript文件加载到浏览器中以及加载它们的顺序。

现在,我们将在IE、Firefox和Safari浏览器上运行测试用例类。

要运行测试用例类,需要启动服务器。您可以使用以下命令行启动JsTestDriver服务器:

java-jarJsTestDriver-1.3.2.jar--port9876--browser"[FirefoxPath]",

"[IEPath]","[SafariPath]"

使用这个命令行,服务器将以Port9876启动,捕获您的机器上的Firefox、IE和Safari浏览器。

启动并捕获浏览器后,可以通过以下命令行运行测试用例类:

java-jarJsTestDriver-1.3.2.jar--testsall

运行命令后,您将看到第一轮结果,如清单6所示。

清单6.第一轮结果

Total9tests(Passed:6;Fails:3;Errors:0)(16.00ms)

Firefox3.6.18Windows:Run3tests(Passed:0;Fails:3;Errors0)(8.00ms)

ApplicationUtilTest.testValidateLoginFormBothEmptyfailed(3.00ms):

AssertError:Usernameisnotvalidatedcorrectly!expected"Thisfield

isrequired"butwas""Error("Usernameisnotvalidatedcorrectly!

expected\"Thisfieldisrequired\"butwas\"\"")@:0()@http://localhost

:9876/test/js-test/TestApplicationUtil.js:16

ApplicationUtilTest.testValidateLoginFormWithEmptyUserNamefailed(3.00ms):

AssertError:Usernameisnotvalidatedcorrectly!expected"Thisfieldis

required"butwas""Error("Usernameisnotvalidatedcorrectly!expected

\"Thisfieldisrequired\"butwas\"\"")@:0()@http://localhost:9876/test

/js-test/TestApplicationUtil.js:29

ApplicationUtilTest.testValidateLoginFormWithEmptyPasswordfailed(2.00ms):

AssertError:Passwordisnotvalidatedcorrectly!expected"Thisfieldis

required"butwas""Error("Passwordisnotvalidatedcorrectly!expected

\"Thisfieldisrequired\"butwas\"\"")@:0()@http://localhost:9876/test/

js-test/TestApplicationUtil.js:42

Safari534.50Windows:Run3tests(Passed:3;Fails:0;Errors0)(2.00ms)

MicrosoftInternetExplorer8.0Windows:Run3tests(Passed:3;Fails:0;

Errors0)(16.00ms)

Testsfailed:Testsfailed.Seelogfordetails.

注意,在清单6中,主要问题出在Firefox上。测试在InternetExplorer和Safari上均可顺利运行。

回页首

修复JavaScript代码并重新运行测试用例

我们来修复损坏的JavaScript代码。我们将使用innerHTML替代innerText。清单7显示了修复后的ApplicationUtil对象代码。

清单7.修复后的ApplicationUtil对象代码

appnamespace={};

appnamespace.ApplicationUtil=function(){};

appnamespace.ApplicationUtil.prototype.validateLoginForm=function(){

varerror=true;

document.getElementById("usernameMessage").innerHTML="";

document.getElementById("passwordMessage").innerHTML="";

if(!document.getElementById("username").value){

document.getElementById("usernameMessage").innerHTML=

"Thisfieldisrequired";

error=false;

}

if(!document.getElementById("password").value){

document.getElementById("passwordMessage").innerHTML=

"Thisfieldisrequired";

error=false;

}

returnerror;

};

使用--testall命令行参数重新运行测试用例对象。清单8显示了第二轮运行结果。

清单8.第二轮运行结果

Total9tests(Passed:9;Fails:0;Errors:0)(9.00ms)

Firefox3.6.18Windows:Run3tests(Passed:3;Fails:0;Errors0)(9.00ms)

Safari534.50Windows:Run3tests(Passed:3;Fails:0;Errors0)(5.00ms)

MicrosoftInternetExplorer8.0Windows:Run3tests(Passed:3;Fails:0;Errors0)

(0.00ms)

如清单8所示,JavaScript代码现在在IE、Firefox和Safari上都能正常运行。

回页首

结束语

在本文中,您了解了如何使用一个最强大的JavaScript单元测试工具(JsTestDriver)在不同的浏览器上测试JavaScript应用程序代码。还了解了什么是JsTestDriver,如何配置它,以及如何在Web应用程序中使用它来确保应用程序的JavaScript代码的质量和可靠性。

相关推荐