如何进行高效JavaScript单元测试

导读:能在一个浏览器上运行的JavaScript并不一定能在其他浏览器上运行。如果没有对代码进行单元测试,那么在决定升级或支持新浏览器的时候,组织就需要花钱测试或重新测试Web应用程序。在本文中,了解JavaScript单元测试如何帮助您降低测试成本,轻松支持更多浏览器。

一个损坏的JavaScript代码示例

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

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

清单 1.登录表单

 
 
  1. <FORM> 
  2.     <table> 
  3.         <tr> 
  4.             <td>Username</td> 
  5.             <td><input type="text" id="username"/></td> 
  6.             <td><span id="usernameMessage"></span></td> 
  7.         </tr> 
  8.         <tr> 
  9.             <td>Password</td> 
  10.             <td><input type="password" id="password"/></td> 
  11.             <td><span id="passwordMessage"></span></td> 
  12.         </tr>     
  13.         <tr> 
  14.             <td><input type="button" onclick="new appnamespace.  
  15.             ApplicationUtil().validateLoginForm()" value="Submit"/></td> 
  16.         </tr> 
  17.     </table> 
  18. </FORM> 

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

清单 2.损坏的ApplicationUtil对象代

 
 
  1. appnamespace = {};  
  2.  
  3. appnamespace.ApplicationUtil = function() {};  
  4.  
  5. appnamespace.ApplicationUtil.prototype.validateLoginForm =  function(){  
  6.     var error = true;  
  7.     document.getElementById("usernameMessage").innerText = "";  
  8.     document.getElementById("passwordMessage").innerText = "";    
  9.  
  10.     if (!document.getElementById("username").value) {  
  11.         document.getElementById("usernameMessage").innerText =   
  12.         "This field is required";  
  13.         error = false;  
  14.     }  
  15.       
  16.     if (!document.getElementById("password").value) {  
  17.         document.getElementById("passwordMessage").innerText =   
  18.         "This field is required";  
  19.         error = false;  
  20.     }         
  21.  
  22.     return error;         
  23. }; 

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

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

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

JsTestDriver

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

图 1.JsTestDriver架构

JsTestDriver 架构

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

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

  1. JavaScript文件,即源文件和测试文件
  2. 配置文件,用于组织源文件和测试文件的加载

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

要使用JsTestDriver库,请先下载最新版的JsTestDriver 1.3.2

jsTestDriver是开源项目

jsTestDriver是Apache 2.0 许可下的一个开源项目,托管在Google Code上,后者是一个类似于SourceForge的项目存储库。只要使用Open Source Initiative批准的许可,开发人员就能在这个存储库中创建和管理公共项目。

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

编写单元测试代码

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

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

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

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

 
 
  1. ApplicationUtilTest = TestCase("ApplicationUtilTest");  
  2.  
  3. ApplicationUtilTest.prototype.setUp = function () {  
  4. /*:DOC += <FORM action=""><table><tr><td>Username</td><td> 
  5. <input type="text" id="username"/></td><td><span id="usernameMessage"> 
  6. </span></td></tr><tr><td>Password</td><td> 
  7. <input type="password" id="password"/></td><td><span id="passwordMessage" 
  8. ></span></td></tr></table></FORM>*/  
  9. };  
  10.  
  11. ApplicationUtilTest.prototype.testValidateLoginFormBothEmpty = function () {  
  12.     var applicationUtil = new appnamespace.ApplicationUtil();  
  13.       
  14.     /* Simulate empty user name and password */  
  15.     document.getElementById("username").value = "";  
  16.     document.getElementById("password").value = "";   
  17.       
  18.     applicationUtil.validateLoginForm();  
  19.       
  20.     assertEquals("Username is not validated correctly!", "This field is required",   
  21.     document.getElementById("usernameMessage").innerHTML);  
  22.     assertEquals("Password is not validated correctly!", "This field is required",   
  23.     document.getElementById("passwordMessage").innerHTML);    
  24. }; 

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

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

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

在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 对象完整代码

 
 
  1. ApplicationUtilTest = TestCase("ApplicationUtilTest");  
  2.  
  3. ApplicationUtilTest.prototype.setUp = function () {  
  4. /*:DOC += <FORM action=""><table><tr><td>Username</td><td> 
  5. <input type="text" id="username"/></td><td><span id="usernameMessage"> 
  6. </span></td></tr><tr><td>Password</td><td> 
  7. <input type="password" id="password"/></td><td><span id="passwordMessage" 
  8. ></span></td></tr></table></FORM>*/  
  9. };  
  10.  
  11. ApplicationUtilTest.prototype.testValidateLoginFormBothEmpty = function () {  
  12.     var applicationUtil = new appnamespace.ApplicationUtil();  
  13.       
  14.     /* Simulate empty user name and password */  
  15.     document.getElementById("username").value = "";  
  16.     document.getElementById("password").value = "";   
  17.       
  18.     applicationUtil.validateLoginForm();  
  19.       
  20.     assertEquals("Username is not validated correctly!", "This field is required",   
  21.     document.getElementById("usernameMessage").innerHTML);  
  22.     assertEquals("Password is not validated correctly!", "This field is required",   
  23.     document.getElementById("passwordMessage").innerHTML);    
  24. };  
  25.  
  26. ApplicationUtilTest.prototype.testValidateLoginFormWithEmptyUserName = function () {  
  27.     var applicationUtil = new appnamespace.ApplicationUtil();  
  28.       
  29.     /* Simulate empty user name and password */  
  30.     document.getElementById("username").value = "";  
  31.     document.getElementById("password").value = "anyPassword";    
  32.       
  33.     applicationUtil.validateLoginForm();  
  34.       
  35.     assertEquals("Username is not validated correctly!",   
  36.     "This field is required", document.getElementById("usernameMessage").innerHTML);  
  37.     assertEquals("Password is not validated correctly!",   
  38.     "", document.getElementById("passwordMessage").innerHTML);    
  39. };  
  40.  
  41. ApplicationUtilTest.prototype.testValidateLoginFormWithEmptyPassword = function () {  
  42.     var applicationUtil = new appnamespace.ApplicationUtil();  
  43.       
  44.     document.getElementById("username").value = "anyUserName";  
  45.     document.getElementById("password").value = "";   
  46.       
  47.     applicationUtil.validateLoginForm();  
  48.       
  49.     assertEquals("Username is not validated correctly!",   
  50.     "", document.getElementById("usernameMessage").innerHTML);  
  51.     assertEquals("Password is not validated correctly!",   
  52.     "This field is required", document.getElementById("passwordMessage").  
  53.     innerHTML);   
  54. }; 

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

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

图 2. JavaScript测试文件夹结构

JavaScript 测试文件夹结构

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

清单 5.JsTestDriver配置文件内容

 
 
  1. server: http://localhost:9876  
  2.  
  3. load:  
  4.   - js-src/*.js  
  5.   - js-test/*.js 

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

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

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

 
 
  1. java -jar JsTestDriver-1.3.2.jar --port 9876 --browser "[Firefox Path]",  
  2.           "[IE Path]","[Safari Path]" 

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

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

 
 
  1. java -jar JsTestDriver-1.3.2.jar --tests all 

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

清单 6.第一轮结果

 
 
  1. Total 9 tests (Passed: 6; Fails: 3; Errors: 0) (16.00 ms)  
  2.   Firefox 3.6.18 Windows: Run 3 tests (Passed: 0; Fails: 3; Errors 0) (8.00 ms)  
  3.     ApplicationUtilTest.testValidateLoginFormBothEmpty failed (3.00 ms):   
  4.     AssertError: Username is not validated correctly! expected "This field   
  5.     is required" but was "" Error("Username is not validated correctly!   
  6.     expected \"This field is required\" but was \"\"")@:0()@http://localhost  
  7.     :9876/test/js-test/TestApplicationUtil.js:16  
  8.  
  9.     ApplicationUtilTest.testValidateLoginFormWithEmptyUserName failed (3.00 ms):   
  10.     AssertError: Username is not validated correctly! expected "This field is   
  11.     required" but was "" Error("Username is not validated correctly! expected   
  12.     \"This field is required\" but was \"\"")@:0()@http://localhost:9876/test  
  13.     /js-test/TestApplicationUtil.js:29  
  14.  
  15.     ApplicationUtilTest.testValidateLoginFormWithEmptyPassword failed (2.00 ms):   
  16.     AssertError: Password is not validated correctly! expected "This field is   
  17.     required" but was "" Error("Password is not validated correctly! expected   
  18.     \"This field is required\" but was \"\"")@:0()@http://localhost:9876/test/  
  19.     js-test/TestApplicationUtil.js:42  
  20.       
  21.   Safari 534.50 Windows: Run 3 tests (Passed: 3; Fails: 0; Errors 0) (2.00 ms)  
  22.   Microsoft Internet Explorer 8.0 Windows: Run 3 tests (Passed: 3; Fails: 0;   
  23.   Errors 0) (16.00 ms)  
  24. Tests failed: Tests failed. See log for details. 

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

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

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

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

 
 
  1. appnamespace = {};  
  2.  
  3. appnamespace.ApplicationUtil = function() {};  
  4.  
  5. appnamespace.ApplicationUtil.prototype.validateLoginForm =  function(){  
  6.     var error = true;  
  7.     document.getElementById("usernameMessage").innerHTML = "";  
  8.     document.getElementById("passwordMessage").innerHTML = "";    
  9.  
  10.     if (!document.getElementById("username").value) {  
  11.         document.getElementById("usernameMessage").innerHTML =   
  12.         "This field is required";  
  13.         error = false;  
  14.     }  
  15.       
  16.     if (!document.getElementById("password").value) {  
  17.         document.getElementById("passwordMessage").innerHTML =   
  18.         "This field is required";  
  19.         error = false;  
  20.     }         
  21.  
  22.     return error;         
  23. }; 

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

清单 8.第二轮运行结果

 
 
  1. Total 9 tests (Passed: 9; Fails: 0; Errors: 0) (9.00 ms)  
  2.   Firefox 3.6.18 Windows: Run 3 tests (Passed: 3; Fails: 0; Errors 0) (9.00 ms)  
  3.   Safari 534.50 Windows: Run 3 tests (Passed: 3; Fails: 0; Errors 0) (5.00 ms)  
  4.   Microsoft Internet Explorer 8.0 Windows: Run 3 tests (Passed: 3; Fails: 0; Errors 0)   
  5.   (0.00 ms) 

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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: JavaScript 单元测试是在编代码之后,用来验证代码功能是否正确的过程。通常使用测试框架来编和运行单元测试,并且会使用断言库来验证代码的结果是否符合预期。 举个例子,假设我们编了一个函数来计算两个数字的和,那么我们可以使用单元测试来验证这个函数的输出是否正确,例如: ``` // 函数定义 function add(a, b) { return a + b; } // 单元测试 it('should return the sum of two numbers', () => { expect(add(2, 3)).toEqual(5); expect(add(-2, 3)).toEqual(1); expect(add(2, -3)).toEqual(-1); }); ``` 在上面的代码中,我们使用了测试框架中的 `it` 函数来定义一个单元测试,并使用断言库中的 `expect` 函数和 `toEqual` 方法来验证函数的输出是否符合预期。 单元测试可以帮助我们更好地理解代码的行为,并且在修改代码后能够快速发现问题。 ### 回答2: JavaScript 单元测试是一种用于测试代码中最小可测试单元的方法。它的目的是确保代码的功能正确,并提高代码的可靠性和可维护性。 在 JavaScript 单元测试中,我们会编一些测试用例来验证代码的不同方面。测试用例通常包括输入数据、预期输出以及要调用的函数或代码块。在执行测试用例时,我们会将输入数据提供给相应的函数或代码块,并检查实际的输出是否和预期输出一致。 JavaScript 单元测试的好处有很多。首先,它可以帮助我们发现代码中的 bug,确保我们的代码在各种情况下都能正常工作。其次,单元测试可以提供一种快速测试代码的方法,以确保之前的修改没有引入新的问题。此外,单元测试还可以提高代码的可维护性,因为每个单元测试都相当于对代码的一份文档,有助于其他开发者理解和修改代码。 有许多 JavaScript 单元测试框架可以用于编和执行单元测试,如Mocha、Jasmine、Jest等。这些框架提供了一些函数和工具,以简化编和执行单元测试的过程。 总的来说,JavaScript 单元测试是一种重要的开发实践,它可以确保我们的代码在各种情况下都能正确工作,并提高代码的可靠性和可维护性。通过使用合适的单元测试框架,我们可以更高效地编和执行单元测试,从而提高代码质量和开发效率。 ### 回答3: JavaScript 单元测试是用来测试JavaScript代码中最小的可测试单元的一种方法。它用于确保代码的正确性和稳定性,并帮助开发人员及时发现和修复代码中的bug。 单元测试通过针对代码中的各个函数、方法和类进行测试,来验证它们的行为是否符合预期。通过编测试用例并运行它们,开发人员可以观察测试结果并判断代码是否能够按照期望的方式运行。这种方式有助于发现隐藏的错误和边界情况,并提供了一种自动化、可靠的方法来验证代码的准确性。 在JavaScript中,常用的单元测试框架有Mocha、Jasmine和Jest等。这些框架提供了丰富的API和功能,可以帮助开发人员编和运行测试用例。测试用例通常包含期望值和实际值的比较,以及对代码行为的验证。开发人员可以编多个测试用例来测试不同的边界情况,从而提高代码的健壮性。 单元测试可以带来许多好处。首先,它可以减少手动测试的工作量,提高开发效率。其次,它可以帮助开发人员更早地发现和修复代码中的错误,减少后期维护的成本。此外,单元测试还可以提高代码的可维护性和可读性,使代码更易于理解和修改。 总之,JavaScript单元测试是一种重要的开发实践,它可以确保代码质量和稳定性,并提供一个自动化的方式来验证代码的正确性。通过编和运行测试用例,开发人员可以更加自信地发布高质量的JavaScript代码。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值