您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
   
 
 
     
   
 订阅
  捐助
javascript单元测试(上)
 
作者:溤淋 来源:博客园 发布于 2015-8-13
   次浏览      
 

1. 什么是单元测试

在计算机编程中,单元测试(又称为模块测试)是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工作。程序单元是应用的最小可测试部件。在过程化编程中,一个单元就是单个程序、函数、过程等;对于面向对象编程,最小单元就是方法,包括基类(超类)、抽象类、或者派生类(子类)中的方法。

每个理想的测试案例独立于其它案例;为测试时隔离模块,经常使用stubs、mock 或fake等测试马甲程序。单元测试通常由软件开发人员编写,用于确保他们所写的代码符合软件需求和遵循开发目标。

单元测试的目标是隔离程序模块并证明这些单个模块是正确的。单元测试能确保在开发过程的早期就能发现问题,是为了让程序“死得更早”。我们应该从开发的早期就为所有函数和方法编写单元测试,可读性强的单元测试可以使程序员方便地检查代码片断是否依然正常工作。良好设计的单元测试案例覆盖程序单元分支和循环条件的所有路径。采用这种自底向上的测试路径,先测试程序模块再测试模块的集合,一旦变更导致错误发生,借助于单元测试可以快速定位并修复错误。

2. JavaScript单元测试现状

单元测试在后台开发中非常流行和普及,比如JAVA开发者的JUnit等,而在前端开发中则使用的非常少。究其原因,主要是单元测试更适用于逻辑代码的测试,这对于JAVA等后台编程语言来说测试起来非常方便,但是前端开发很多时候要要UI打交道,UI相关的代码不是不可以进行单元测试,但的确很麻烦,比起逻辑代码来说困难多了,这就导致了单元测试在前端开发没有普及起来。

但是随着单元测试的普及,尤其是敏捷开发的推动,涌现了许多优秀的JavaScript单元测试框架,如QUnit、Jasmine等。所有的这些框架基本上都能对Javascript代码进行很好的测试,当然UI部分的代码测试一样比较麻烦,但是我们可以通过精心构造我们的测试代码来测试部分UI代码。但是每个框架都不是万能的,它们都有各自擅长的领域,下面选取了几个具有代表性的框架进行介绍。

3. 单元测试常用框架

QUnit框架

a) 简介

QUnit是jQuery团队开发的JavaScript单元测试工具,功能强大且使用简单。目前所有的JQuery代码都使用QUnit进行测试,原生的JavaScript也可以使用QUnit。

最初,John Resig将QUnit设计为jQuery的一部分。2008年,QUnit才有了自己的名字、主页和API文档,也开始允许其他人用它来做单元测试。但当时QUnit还是基于jQuery的。直到2009年,QUnit才可以完全的独立运行。

b) 优点

使用起来非常方便,有漂亮的外观和完整的测试功能(包括异步测试);

非常简单,容易上手,目前公开的API只有19个;

不需要依赖其它任何软件包或框架,只要能运行JS的地方就可以,QUnit本身只有一个JS文件和CSS文件,当然如果需要可以和jQuery等其它框架集成;

不仅支持在浏览器中测试,还支持在Rhino和node.js等后端测试

c) 不足

对自动化支持不好,很难和Ant、Maven或自动构建等工具集成,主要用在浏览器中进行测试。

d) API

QUnit所有的API可以分为三类:Setup,Assertions,Asynchronous Testing,下面就分别对这些API做些介绍:

Setup:

test( name, [expected], testFun ) 代表QUnit中的一个测试

name:要测试的名称,比如“加法函数”或“add”等

expected:可选参数,用来表示该测试函数的断言的数量,是个正整数

testFun:一个函数,所有的测试代码都应该包括在该函数里,通常这是一个匿名函数。

例:

test(“add function”, 1, function() {
equal(add(1, 2), 3);
});

asyncTest( name, [expected], testFun ) 代表QUnit中的一个异步测试,参数同test

expect( amount ) 用在测试函数中,用于声明测试断言的数量,这个函数和test中的expected参数的作用是一样的。主要作用就是检查你声明的个数和你写的断言的实际个数是否一致。

module( name, [lifecycle] ) 主要用于测试函数的分组,一个module函数为一个分组,比如module(“validate”)表示后面的测试用例都是validate相关的代码,或者module(“common.js”),表明后面的测试用例都是common.js里面的代码。一个测试文件可以写多个module。

name:分组或者模块的名称

lifecycle:可选参数,它是一个对象,可以设置setup和teardown回调函数

例:

module(“common.js”, 
    {
        setup:function(){},
        teardown: function() {} 
    }
);

setup:在module开始之前执行,可以为该module下面的测试代码做一些准备工作

teardown:将会在该module的所有测试代码执行后执行,比如做一些清理还原工作等。

QUnit.init( ) 用于初始化QUnit测试框架,通常这个函数是不需要我们手工调用的。

QUnit.reset( ) 重设函数,通常是在每个test函数执行后由QUnit自己调用来重设整个QUnit测试环境,当然必要时我们自己也可以调用它来复原,不常用。

Assertions:

ok( state, [message] ) 断言。state值为true时表示通过,否则失败。

equal( actual, expected, [message] ) 比较参数actual和expected是否相等,相当于 ==

notEqual( actual, expected, [message] ) 比较两个参数是否不相等,相当于 !=

deepEqual( actual, expected, [message] ) 主要用于数组和对象等类型的值是否相等,会递归遍历它们所包含的值是否相等。

notDeepEqual( actual, expected, [message] ) 主要用于数组和对象等类型的值是否不相等,会递归遍历它们所包含的值是否不相等。

strictEqual( actual, expected, [message] ) 比较两个参数是否严格相等,相当于 ===

notStrictEqual( actual, expected, [message] ) 比较两个参数是否不严格相等,相当于 !==

throws( block, expected, [message] ) 测试block函数是否抛出一个异常,抛出则通过,不抛则失败。

block:我们要测试的函数

expected:可选参数,是一个类型,用来验证第一个函数抛出的异常是否是我们预期的类型。

例:


function CustomError( message ) {

this.message = message;

}

CustomError.prototype.toString = function() {

return this.message;

};

throws(

function() {

throw new CustomError(“some error description”);

},

CustomError,

"raised error is an instance of CustomError"

);

  

Asynchronous Testing:

stop( [increment] ) 停止测试的运行,用于异步测试。在异步测试时一般先把QUnit的test runner停下来。

increment:增加停止的时间。

start( [decrement] ) 当异步调用成功后就应该把停止的test runner启动起来让它接着往前跑

decrement:用来减少停止的时间。

例:

test( "a test", function() {

    stop();

    var result = null;

    $.ajax(

        url,

        {},

        function(data){

            result = data;

        }

    );

    setTimeout(function() {

        equals(result, "success" );

        start();

    }, 150 );

});

 

e) 使用

test.html

导入qunit.css,qunit.js

依次导入被测试文件src.js和测试文件test.js

src.js里是我们要测试的一些函数

test.js里放我们的测试

打开test.html,显示:

如果期望值与函数执行的结果不一致,会报错:

test.js

test.html显示:

期望值与结果不符,测试不通过。

与浏览器自动化测试工具集成的接口:

都是QUnit自动调用的一些函数,一般不用改,也可以自己定制

QUnit.log(Function({ result, actual, expected, message })) 这个接口会在每个断言执行后被自动调用

result:断言是否通过

message:断言里的message参数

例:

QUnit.log(function(details){

    alert(“Log: ” + details.result + “ ” + details.message);

})

QUnit.testStart(Function({ name })) 在每个测试函数执行前被自动调用

name:测试函数中的name参数值

QUnit.testDone(Function({ name, failed, passed, total })) 在每个测试函数结束后执行被自动调用

name:同上

failed:指失败断言的个数

passed:指成功断言的个数

total:所有断言的个数

QUnit.moduleStart(Function({ name })) 在每个module所有的测试代码执行前被自动调用

name:module函数中name参数的值

QUnit.moduleDone(Function({ name, failed, passed, total })) 在每个module所有的测试代码执行完之后被自动执行

failed:指失败断言的个数

passed:指成功断言的个数

total:指所有断言的个数

QUnit.begin(Function()) 在所有的测试代码调用之前运行

QUnit.done(Function({ failed, passed, total, runtime })) 在所有的测试代码调用之后运行

failed:指失败断言的个数

passed:指成功断言的个数

total:指所有断言的个数

runtime:所有代码的执行时间

Jasmine框架

a) 简介

Jasmine是一个有名的JavaScript单元测试框架,它是独立的行为驱动开发框架,语法清晰易懂。

行为驱动开发(BDD):是一种敏捷软件开发的技术,它鼓励软件项目中的开发者、QA和非技术人员或商业参与者之间的协作。BDD最初是由Dan North在2003年命名,它包括验收和客户测试驱动等的极限编程的实践,作为对测试驱动开发的回应。在过去的数年里,得到了极大的发展。

BDD的重点是通过与利益相关者的讨论取得对预期的软件行为的清醒认识。它通过用自然语言书写非程序员可读的测试用例扩展了测试驱动开发方法。行为驱动开发人员使用混合了领域中统一的语言的母语语言来描述他们的代码的目的。这让开发者得以把精力集中在代码应该怎么写,而不是技术细节上,而且也最大程度的减少了将代码编写者的技术语言与商业客户、用户、利益相关者、项目管理者等的领域语言之间来回翻译的代价。

BDD的做法包括:

确立不同利益相关者要实现的远景目标

使用特性注入方法绘制出达到这些目标所需要的特性

通过由外及内的软件开发方法,把涉及到的利益相关者融入到实现的过程中

使用例子来描述应用程序的行为或代码的每个单元

通过自动运行这些例子,提供快速反馈,进行回归测试

使用“应当(should)”来描述软件的行为,以帮助阐明代码的职责,以及回答对该软件的功能性的质疑

使用“确保(ensure)”来描述软件的职责,以把代码本身的效用与其他单元(element)代码带来的边际效用中区分出来。

使用mock作为还未编写的相关代码模块的替身

BDD特性注入:一个公司可能有多个会带来商业利益的不同愿景,通常包括盈利、省钱或保护钱。一旦某个愿景被开发小组确定为当前条件下的最佳愿景,他们将需要更多的帮助来成功实现这个远景。

然后确定该愿景的主要利益相关者,会带入其他的利益相关者。每个相关者要定义为了实现该愿景他们需要完成的目标。例如,法务部门可能要求某些监管要得到满足。市场营销负责人可能要参加将使用该软件的用户的社区。安全专家需要确保该软件不会受到SQL注入的攻击。

通过这些目标,会定义出要实现这些目标所需要的大概的题目或者特性集合。例如,“允许用户排序贡献值”或“交易审计”。从这些主题,可以确定用户功能以及用户界面的第一批细节。

b) 优点

它是基于行为驱动开发实现的测试框架,它的语法非常贴近自然语言,简单明了,容易理解。

能很方便的和Ant、Maven等进行集成进行自动化测试,也可以方便和Jekins等持续集成工具进行集成,可以生成测试结果的XMl文档。

它有丰富的API,同时用户也支持用户扩展它的API,这一点很少有其它框架能够做到。

使用方便简单,只需要引入两个js文件即可

不仅支持在浏览器中测试,还支持在Rhino和node.js等后端测试。

对于Ruby语言有特别的支持,能够非常方便的集成到Ruby项目中去

c) 不足

在浏览器中的测试界面不如QUnit美观、详细。

d) API

it(string, function) 一个测试Spec

string:测试名称

function:测试函数

describe (string, function) 一个测试组开始于全局函数describe,一个describe是一个it的集合。describe包含n个it,一个it包含n个判断断言 Suite

string:测试组名称

function:测试组函数

describe("测试add()函数", function() {
    it("1 + 1 = 2", function(){
        expect(add(1, 1)).toBe(2);
    });
});

beforeEach(function) 定义在一个describe的所有it执行前做的操作

afterEach(function) 定义在一个describe的所有it执行后做的操作

expect(a).matchFunction(b)

expect(a).not.matchFunction(b) 期望a和b满足匹配方式matchFunction

matchFunctions:

toBe 相当于===,处理简单字面值和变量

it("toBe相当于===", function(){
    var a = 12;
    var b = a;
    expect(a).toBe(b);
    expect(a).not.toBe(null);
    expect(false == 0).toBe(true);
});
it("toBe不能当==用", function(){
    expect(false).toBe(0);
});

toEqual 处理简单字面值和变量,而且可以处理对象,数组

it("toEqual可以处理字面值,变量和对象", function(){
    var a = 12;
    expect(a).toEqual(12);
    var foo = {key : "key"};
    var bar = {key : "key"};
    expect(foo).toEqual(bar);
    var arr1 = [];
    arr1["p1"] = "string1";
    var arr2 = [];
    arr2["p1"] = "string1";
    var obj = {};
    obj["p1"] = "string1";
    expect(arr1).toEqual(arr2);
    expect(arr1).toEqual(obj);
})

toMatch 按正则式检索。

it("toMatch匹配正则式", function(){

    var message = "foo bar baz";

    expect(message).toMatch(/bar/);

    expect(message).toMatch("bar");

    expect(message).not.toMatch(/quux/);
    expect(message).toMatch(/^f/);
    expect(message).not.toMatch(/f$/);
});

toBeDefined 是否已声明且赋值

it("toBeDefined检测变量非undefined", function(){

    var a = { key : "key"};

    expect(a.key).toBeDefined();

    expect(a.foo).not.toBeDefined();

    //expect(c).not.toBeDefined();  //未声明出错

    var b;

    expect(b).not.toBeDefined();

});

对象.未声明属性.not.toBeDefined(); 通过

未声明变量.not.toBeDefined(); 报错

toBeUndefined 是否undefined

toBeNull 是否null

toBeTruthy 如果转换为布尔值,是否为true

toBeFalsy 如果转换为布尔值,是否为false

toContain 数组中是否包含元素(值)。只能用于数组,不能用于对象


it("toContain检验数组中是否包含元素(值)", function(){

var a = ["foo", "bar", "baz"];

expect(a).toContain("bar");

});

toBeLessThan 数值比较,小于

toBeGreaterThan 数值比较,大于

toBeCloseTo 数值比较时定义精度,先四舍五入后再比较


it("toBeCloseTo数值比较,指定精度,先四舍五入再比较", function() {

var pi = 3.1415926, e = 2.78;

expect(pi).toBeCloseTo(e, 0);

expect(pi).not.toBeCloseTo(e, 0.1);

});

   
次浏览       
相关文章

微服务测试之单元测试
一篇图文带你了解白盒测试用例设计方法
全面的质量保障体系之回归测试策略
人工智能自动化测试探索
相关文档

自动化接口测试实践之路
jenkins持续集成测试
性能测试诊断分析与优化
性能测试实例
相关课程

持续集成测试最佳实践
自动化测试体系建设与最佳实践
测试架构的构建与应用实践
DevOps时代的测试技术与最佳实践
最新活动计划
LLM大模型应用与项目构建 12-26[特惠]
QT应用开发 11-21[线上]
C++高级编程 11-27[北京]
业务建模&领域驱动设计 11-15[北京]
用户研究与用户建模 11-21[北京]
SysML和EA进行系统设计建模 11-28[北京]

LoadRunner性能测试基础
软件测试结果分析和质量报告
面向对象软件测试技术研究
设计测试用例的四条原则
功能测试中故障模型的建立
性能测试综述
更多...   

性能测试方法与技术
测试过程与团队管理
LoadRunner进行性能测试
WEB应用的软件测试
手机软件测试
白盒测试方法与技术

某博彩行业 数据库自动化测试
IT服务商 Web安全测试
IT服务商 自动化测试框架
海航股份 单元测试、重构
测试需求分析与测试用例分析
互联网web测试方法与实践
基于Selenium的Web自动化测试
更多...