如何进行高效JavaScript单元测试

如何进行高效JavaScript单元测试

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

  一个损坏的JavaScript代码示例

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

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

  清单 1.登录表单

<FORM> 
    <table> 
        <tr> 
            <td>Username</td> 
            <td><input type="text" id="username"/></td> 
            <td><span id="usernameMessage"></span></td> 
        </tr> 
        <tr> 
            <td>Password</td> 
            <td><input type="password" id="password"/></td> 
            <td><span id="passwordMessage"></span></td> 
        </tr>     
        <tr> 
            <td><input type="button" onclick="new appnamespace.  
            ApplicationUtil().validateLoginForm()" value="Submit"/></td> 
        </tr> 
    </table> 
</FORM>

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

  清单 2.损坏的ApplicationUtil对象代

appnamespace = {};  

appnamespace.ApplicationUtil = function() {};  

appnamespace.ApplicationUtil.prototype.validateLoginForm =  function(){  
    var error = true;  
    document.getElementById("usernameMessage").innerText = "";  
    document.getElementById("passwordMessage").innerText = "";    

    if (!document.getElementById("username").value) {  
        document.getElementById("usernameMessage").innerText =   
        "This field is required";  
        error = false;  
    }  
      
    if (!document.getElementById("password").value) {  
        document.getElementById("passwordMessage").innerText =   
        "This field is required";  
        error = false;  
    }         

    return error;         
};

  在清单 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架构

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

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

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

  这个架构比较灵活,允许单个服务器从网络中的其他机器捕获任意数量的浏览器。例如,如果您的代码在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 对象代码的一部分

ApplicationUtilTest = TestCase("ApplicationUtilTest");  
 
ApplicationUtilTest.prototype.setUp = function () {  
/*:DOC += <FORM action=""><table><tr><td>Username</td><td> 
<input type="text" id="username"/></td><td><span id="usernameMessage"> 
</span></td></tr><tr><td>Password</td><td> 
<input type="password" id="password"/></td><td><span id="passwordMessage" 
></span></td></tr></table></FORM>*/  
};  
 
ApplicationUtilTest.prototype.testValidateLoginFormBothEmpty = function () {  
    var applicationUtil = new appnamespace.ApplicationUtil();  
      
    /* Simulate empty user name and password */  
    document.getElementById("username").value = "";  
    document.getElementById("password").value = "";   
      
    applicationUtil.validateLoginForm();  
      
    assertEquals("Username is not validated correctly!", "This field is required",   
    document.getElementById("usernameMessage").innerHTML);  
    assertEquals("Password is not validated correctly!", "This field is required",   
    document.getElementById("passwordMessage").innerHTML);    
};

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

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

  在testValidateLoginFormBothEmpty方法中,创建了一个ApplicationUtil对象,并在测试用例方法中使用该对象。然后,代码通过检索用户名和密码的DOM元素并将它们的值设置为空值来模拟输入空用户名和密码。可以调用validateLoginForm方法来执行实际表单验证。最后,将调用assertEquals来确保usernameMessage 和 passwordMessage 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 对象完整代码

ApplicationUtilTest = TestCase("ApplicationUtilTest");  
 
ApplicationUtilTest.prototype.setUp = function () {  
/*:DOC += <FORM action=""><table><tr><td>Username</td><td> 
<input type="text" id="username"/></td><td><span id="usernameMessage"> 
</span></td></tr><tr><td>Password</td><td> 
<input type="password" id="password"/></td><td><span id="passwordMessage" 
></span></td></tr></table></FORM>*/  
};  
 
ApplicationUtilTest.prototype.testValidateLoginFormBothEmpty = function () {  
    var applicationUtil = new appnamespace.ApplicationUtil();  
      
    /* Simulate empty user name and password */  
    document.getElementById("username").value = "";  
    document.getElementById("password").value = "";   
      
    applicationUtil.validateLoginForm();  
      
    assertEquals("Username is not validated correctly!", "This field is required",   
    document.getElementById("usernameMessage").innerHTML);  
    assertEquals("Password is not validated correctly!", "This field is required",   
    document.getElementById("passwordMessage").innerHTML);    
};  
 
ApplicationUtilTest.prototype.testValidateLoginFormWithEmptyUserName = function () {  
    var applicationUtil = new appnamespace.ApplicationUtil();  
      
    /* Simulate empty user name and password */  
    document.getElementById("username").value = "";  
    document.getElementById("password").value = "anyPassword";    
      
    applicationUtil.validateLoginForm();  
      
    assertEquals("Username is not validated correctly!",   
    "This field is required", document.getElementById("usernameMessage").innerHTML);  
    assertEquals("Password is not validated correctly!",   
    "", document.getElementById("passwordMessage").innerHTML);    
};  
 
ApplicationUtilTest.prototype.testValidateLoginFormWithEmptyPassword = function () {  
    var applicationUtil = new appnamespace.ApplicationUtil();  
      
    document.getElementById("username").value = "anyUserName";  
    document.getElementById("password").value = "";   
      
    applicationUtil.validateLoginForm();  
      
    assertEquals("Username is not validated correctly!",   
    "", document.getElementById("usernameMessage").innerHTML);  
    assertEquals("Password is not validated correctly!",   
    "This field is required", document.getElementById("passwordMessage").  
    innerHTML);   
};

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

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

  图 2. 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 -jar JsTestDriver-1.3.2.jar --port 9876 --browser "[Firefox Path]",  
          "[IE Path]","[Safari Path]"

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

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

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

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

  清单 6.第一轮结果

Total 9 tests (Passed: 6; Fails: 3; Errors: 0) (16.00 ms)  
  Firefox 3.6.18 Windows: Run 3 tests (Passed: 0; Fails: 3; Errors 0) (8.00 ms)  
    ApplicationUtilTest.testValidateLoginFormBothEmpty failed (3.00 ms):   
    AssertError: Username is not validated correctly! expected "This field   
    is required" but was "" Error("Username is not validated correctly!   
    expected \"This field is required\" but was \"\"")@:0()@http://localhost  
    :9876/test/js-test/TestApplicationUtil.js:16  
 
    ApplicationUtilTest.testValidateLoginFormWithEmptyUserName failed (3.00 ms):   
    AssertError: Username is not validated correctly! expected "This field is   
    required" but was "" Error("Username is not validated correctly! expected   
    \"This field is required\" but was \"\"")@:0()@http://localhost:9876/test  
    /js-test/TestApplicationUtil.js:29  
 
    ApplicationUtilTest.testValidateLoginFormWithEmptyPassword failed (2.00 ms):   
    AssertError: Password is not validated correctly! expected "This field is   
    required" but was "" Error("Password is not validated correctly! expected   
    \"This field is required\" but was \"\"")@:0()@http://localhost:9876/test/  
    js-test/TestApplicationUtil.js:42  
      
  Safari 534.50 Windows: Run 3 tests (Passed: 3; Fails: 0; Errors 0) (2.00 ms)  
  Microsoft Internet Explorer 8.0 Windows: Run 3 tests (Passed: 3; Fails: 0;   
  Errors 0) (16.00 ms)  
Tests failed: Tests failed. See log for details.

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

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

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

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

appnamespace = {};  
 
appnamespace.ApplicationUtil = function() {};  
 
appnamespace.ApplicationUtil.prototype.validateLoginForm =  function(){  
    var error = true;  
    document.getElementById("usernameMessage").innerHTML = "";  
    document.getElementById("passwordMessage").innerHTML = "";    
 
    if (!document.getElementById("username").value) {  
        document.getElementById("usernameMessage").innerHTML =   
        "This field is required";  
        error = false;  
    }  
      
    if (!document.getElementById("password").value) {  
        document.getElementById("passwordMessage").innerHTML =   
        "This field is required";  
        error = false;  
    }         
 
    return error;         
};

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

  清单 8.第二轮运行结果

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

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

  结束语

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

本文出自seven的测试人生公众号最新内容请见作者的GitHub页:http://qaseven.github.io/

时间: 2024-05-17 02:05:26

如何进行高效JavaScript单元测试的相关文章

如何寻找一个好的Javascript单元测试工具

让我们看看Javascript单元测试的现状和测试工具. 1.JsUnit 我们已经在使用它来作为我们的单元测试工具了. 优势: ●可以被ant构建文件调用: ●启动浏览器来运行测试用例: ●Eclipse插件支持: 缺点: ●需要启动浏览器来运行测试用例: ●不支持用js文件来写单元测试代码:它必须嵌入在html文件中: ●它已经好几年没更新了: 注意: ●有一个测试工具叫JsUnit(2): ●"ant"是一个开源构建工具:之所以叫"Ant"因为它很小,但是可以

使用Mootools开发JavaScript单元测试框架

Mootools是一个很好的JavaScript框架,它简洁.模块化,使用很简单,开发人员能够很快上手,编写扩展能力强.兼容性好的JavaScript代码.我这两天用了用,感觉和Prototype用法简直一模一样,不过功能似乎更加强,有很多效果,拖拽等.而且它的面向对象,继承比Prototype更直观好用,所以我开始学习这个框架. 首先看看Mootools的基本用法吧,更加详尽的解释可以在官方网站上找到. 1.? 如果我们要写一个Animal类,我们可以这样写: var Animal = new

javascript单元测试怎么应用在实际的系统中?

问题描述 javascript单元测试怎么应用在实际的系统中? 希望自己写的代码不那么垃圾,所以今天学了学js的单元测试,有一些疑问. 1 我怎么测试模块中封装好的函数. 我看Qunit教程中是把那个js文件加载到测试页面,所有的函数都是可以直接调用的.但是我现在用的ExtJs,我想测试一个模块中的函数,但这个函数被封装在Ext的对象当中,我怎么测试啊. 2 测试的函数依赖很多其他函数和库. 这种情况怎么办?要把所有依赖的函数js都引用进测试页面吗?假如都要引用进来,不是和写个console.l

Javascript单元测试框架QUnitjs详细介绍_javascript技巧

一.什么是 QUnit QUnit(http://qunitjs.com/) 是一个非常强大的javascript单元测试框架,可以帮你调试代码.它是由 jQuery 团队的成员写的,而且是 jQuery 的官方测试套装.但QUnit一般是足以测试任何常规 javascript 代码,它甚至可能通过一些 javascript 引擎比如 Rhino 或 V8 来测试服务器端 JavaScript.如果你不熟悉"单元测试"的概念,请不要担心.这不是很难理解的: 复制代码 代码如下: 在计算

如何搭建自动化、跨浏览器的 JavaScript 单元测试

本文讲的是如何搭建自动化.跨浏览器的 JavaScript 单元测试, 我们都知道在各个不同的浏览器环境里测试代码是很重要的,并且在大多数时候,我们这些 Web 开发者在这一点上还是做的不错的 -- 至少在第一次发布项目的时候是这样. 然而我们每次更改代码之后的测试工作,却做的不尽人意. 我深切地知道我本人就是这样的 -- 我早就把"学习怎样搭建自动化.跨浏览器的 JavaScript 单元测试"写在 To-do List 上了,但每当我坐下来想要真正的去解决这个问题的时候,我却不得不

javascript-关于集成karma后的JavaScript单元测试配置的问题

问题描述 关于集成karma后的JavaScript单元测试配置的问题 小弟初学js的单元测试,使用的是karma+chai+phantomJS,进行的单元测试.现在的架构搭建起来,对应的文件也有,但是使用karama start这个命令的时候,能够看到phantomJS已经连接上,但是不加载对应的测试文件,求教应该怎么处理,图片稍后附上 解决方案 参考下http://www.aichengxu.com/view/34311http://www.tuicool.com/articles/yuMv

简单易操作的跨浏览器JavaScript单元测试解决方案

关于单元测试 前端的单元测试也可以称为自动化测试,测试驱动开发,单元测试对于前端模块化.框架和功能库的开发是非常有必要的,只要做好模块的解耦和功能划分,单元测试就可以愉快地进行.好的单元测试(全面的功能.抛错和边缘覆盖)可以成为项目开发或修改完成后是否能"安全上线"的重要判断依据之一. 引进跨平台测试 开发和运行单元测试通常是在开发人员的电脑上完成,而任何一台电脑上所能安装的浏览器是远远不能满足测试兼容性需求的,如果需要配备各种平台的电脑也是非常浪费人力物力.那么有没有工具能自动把本地

JQuery团队打造的javascript单元测试工具QUnit介绍_jquery

什么是单元测试? 单元测试又称为模块测试,是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工作.单元测试主要是用来检验程式的内部逻辑,也称为个体测试.结构测试或逻辑驱动测试.通常由撰写程式码的程式设计师负责进行. 通常来说,程式設計師每修改一次程式就會進行最少一次單元測試,在編寫程式的過程中前後很可能要進行多次單元測試,以證實程式達到軟件規格書(en:Specification)要求的工作目標,沒有臭蟲:雖然单元测试不是什么必须的,但也不坏,這牽涉到專案管理的政策決定. -- 维基百科

Facebook推出JavaScript单元测试和自动化Mock工具Jest

Facebook发布了Jest,一个开源的.基于Jasmine框架的JavaScript单元测试工具. Jest源于Facebook两年前的构想,用于快速.可靠地测试Web聊天应用.它吸引了公司内部的兴趣,Facebook的一名软件工程师Jeff Morrison半年前又重拾这个项目,改善它的性能,并将其开源. 在最基础层面,Jest被设计用于快速.简单地编写地道的JavaScript测试.Jest自动模拟require()返回的CommonJS模块,并提供了包括内置的测试环境Dom API支持