[转]javascript单元测试

1.      什么是单元测试

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

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

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

2.      JavaScript单元测试现状

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

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

 

3.      单元测试经常使用框架

l   QUnit框架

a)        简介

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

最初,John Resig将QUnit设计为jQuery的一部分。2008年,QUnit才有了本身的名字、主页和API文档,也开始容许其余人用它来作单元测试。但当时QUnit仍是基于jQuery的。直到2009年,QUnit才能够彻底的独立运行。git

b)       优势

使用起来很是方便,有漂亮的外观和完整的测试功能(包括异步测试);程序员

很是简单,容易上手,目前公开的API只有19个;github

不须要依赖其它任何软件包或框架,只要能运行JS的地方就能够,QUnit自己只有一个JS文件和CSS文件,固然若是须要能够和jQuery等其它框架集成;web

不只支持在浏览器中测试,还支持在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:全部代码的执行时间

 

API及下载:http://api.qunitjs.com/

参考:http://www.weakweb.com/articles/255.html

http://www.iteye.com/topic/981253

 

l  Jasmine框架

a)        简介

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

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

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

 

BDD的作法包括:

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

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

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

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

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

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

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

l  使用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);
    });

 

toThrow    检验一个函数是否会抛出一个错误

    it("toThrow检验一个函数是否会抛出一个错误", function() {
        var foo = function() {
          return 1 + 2;
        };
        var bar = function() {
          return a + 1;
        };

        expect(foo).not.toThrow();
        expect(bar).toThrow();
    });

 

注:describe可嵌套

       xdescribe 和 xit:路过不执行,结果不显示。像display:none。点控制栏中skipped显示

      

Spy   存储函数的被调用状况和参数(函数监视器,记录被调用状况,但函数并不真执行)

describe("对spy函数的测试", function() {
    var foo, bar = null;

    beforeEach(function() {
        foo = {
            setBar: function(value) {
                bar = value;
            }
        };

        spyOn(foo, 'setBar');  //foo为spy函数

        foo.setBar(123);
        foo.setBar(456, 'another param');
    });

    it("测试foo函数是否被调用过", function() {
        expect(foo.setBar).toHaveBeenCalled();
    });

    it("测试foo函数被调用的次数", function() {
        expect(foo.setBar.calls.length).toEqual(2);
    });

    it("测试foo函数被调用时传入的参数", function() {
        expect(foo.setBar).toHaveBeenCalledWith(123);
        expect(foo.setBar).toHaveBeenCalledWith(456, 'another param');
    });

    it("上一次被调用的参数", function() {
        expect(foo.setBar.mostRecentCall.args[0]).toEqual(456);
    });

    it("全部被调用的状况存在一个数组里", function() {
        expect(foo.setBar.calls[0].args[0]).toEqual(123);
    });

    it("函数并未真的执行", function() {  
        expect(bar).toBeNull();  
    });
});

Spy addCallThrough  函数监视器,但函数真的执行

describe("对spy函数的测试,函数真的执行", function() {
    var foo, bar, fetchedBar;

    beforeEach(function() {
        foo = {
            setBar: function(value) {
                bar = value;
            },
            getBar: function() {
                return bar;
            }
        };

        //spyOn(foo, "setBar");    //若是加上这句,setBar不真的执行,后两个spec不经过
        spyOn(foo, 'getBar').andCallThrough();

        foo.setBar(123);
        fetchedBar = foo.getBar();
    });

    it("测试foo中getBar函数是否被调用过", function() {
        expect(foo.getBar).toHaveBeenCalled();
    });

    it("foo中setBar函数真的执行了", function() {
        expect(bar).toEqual(123);
    });

    it("foo中getBar函数真的执行了", function() {
        expect(fetchedBar).toEqual(123);
    });
});

Spy andReturn  函数监视器,函数不真的执行。指定监视的函数的返回值

describe("A spy, when faking a return value", function() {
    var foo, bar, fetchedBar;

    beforeEach(function() {
        foo = {
            setBar: function(value) {
                bar = value;
            },
            getBar: function() {
                return bar;
            }
        };

        spyOn(foo, 'getBar').andReturn(745);  //指定getBar函数返回745

        foo.setBar(123);
        fetchedBar = foo.getBar();
    });

    it("测试foo中getBar函数是否被调用过", function() {
        expect(foo.getBar).toHaveBeenCalled();
    });

    it("不影响未被监视的其它函数", function() {
        expect(bar).toEqual(123);
    });

    it("指定的返回值745", function() {
        expect(fetchedBar).toEqual(745);
    });
});

Spy addCallFake  替代被监视的函数,原函数不执行

describe("替代被监视的函数,原函数不执行", function() {
    var foo, bar, fetchedBar;

    beforeEach(function() {
        foo = {
            setBar: function(value) {
                bar = value;
            },
            getBar: function() {
                alert("frostbelt");
                return bar;
            }
        };

        spyOn(foo, 'getBar').andCallFake(function() {
            return 1001;
        });

        foo.setBar(123);
        fetchedBar = foo.getBar();
    });

    it("测试foo中getBar函数是否被调用过", function() {
        expect(foo.getBar).toHaveBeenCalled();
    });

    it("不影响未被监视的其它函数", function() {
        expect(bar).toEqual(123);
    });

    it("getBar被addCallFake指定的匿名函数代替,getBar不执行", function() {
        expect(fetchedBar).toEqual(1001);
    });
});

若是你没有什么可监视的又实在想监视一下,该咋办?本身create一个被监视函数。。

jasmine.createSpy(functionId)

describe("本身造一个被监视函数。啊,我凌乱了。。", function() {
    var whatAmI;

    beforeEach(function() {
        whatAmI = jasmine.createSpy('whatAmI');

        whatAmI("I", "am", "a", "spy");
    });

    it("有个id,是createSpy的传入函数,用于报错", function() {
        expect(whatAmI.identity).toEqual('whatAmI')
    });

    it("是否被调用", function() {
        expect(whatAmI).toHaveBeenCalled();
    });

    it("被调用的次数", function() {
        expect(whatAmI.calls.length).toEqual(1);
    });

    it("被调用的参数", function() {
        expect(whatAmI).toHaveBeenCalledWith("I", "am", "a", "spy");
    });

    it("最近一次被调用", function() {
        expect(whatAmI.mostRecentCall.args[0]).toEqual("I");
    });
});

有时须要监视一个对象的不少方法,用createSpyObj添加方法数组

jasmine.createSpyObj(obj, methodArray)

describe("有时须要监视一个对象的不少个方法,用createSpyObj添加数组", function() {
    var tape;

    beforeEach(function() {
        tape = jasmine.createSpyObj('tape', ['play', 'pause', 'stop', 'rewind']);

        tape.play();
        tape.pause();
        tape.rewind(0);
    });

    it("tape对象的这四个方法已被定义", function() {
        expect(tape.play).toBeDefined();
        expect(tape.pause).toBeDefined();
        expect(tape.stop).toBeDefined();
        expect(tape.rewind).toBeDefined();
    });

    it("四个方法是否被调用", function() {
        expect(tape.play).toHaveBeenCalled();
        expect(tape.pause).toHaveBeenCalled();
        expect(tape.rewind).toHaveBeenCalled();
        expect(tape.stop).not.toHaveBeenCalled();
    });

    it("被调用时传入的参数", function() {
        expect(tape.rewind).toHaveBeenCalledWith(0);
    });
});

 

jasmine.any   类型判断。instanceof

describe("类型匹配", function() {
    it("至关于instanceof", function() {
        expect({}).toEqual(jasmine.any(Object));
        expect(12).toEqual(jasmine.any(Number));
    });

    it("也能够用于spy", function() {
        var foo = jasmine.createSpy('foo');
        foo(12, function() {
            return true
        });

        expect(foo).toHaveBeenCalledWith(jasmine.any(Number), jasmine.any(Function));
        //foo被调用时的参数 类型判断
    });
});

jasmine.Clock.useMock()   jasmine本身控制时间,实现异步调试,减小等待

jasmine.Clock.tick(n:uint)   向前n毫秒

describe("jasmine本身控制时间,实现异步调试,减小等待", function() {
    var timerCallback; 

    beforeEach(function() {
        timerCallback = jasmine.createSpy('timerCallback');
        jasmine.Clock.useMock();
    }); 

    it("setTimeout", function() {
        setTimeout(function() {
            timerCallback();
        }, 100);

        expect(timerCallback).not.toHaveBeenCalled();

        jasmine.Clock.tick(101);

        expect(timerCallback).toHaveBeenCalled();
    });

    it("setInterval", function() {
        setInterval(function() {
            timerCallback();
        }, 100);

        expect(timerCallback).not.toHaveBeenCalled();

        jasmine.Clock.tick(101);
        expect(timerCallback.callCount).toEqual(1);

        jasmine.Clock.tick(50);
        expect(timerCallback.callCount).toEqual(1);

        jasmine.Clock.tick(50);
        expect(timerCallback.callCount).toEqual(2);
    });
});

注:在这种环境下setTimeout和setInterval的callback为同步的,系统时间再也不影响执行

 

runs(function)  waitsFor(function, message, millisec)   Jasmine异步调试  按本身的理解写个例子

describe("jasmine异步调试,对ajax结果的断言", function(){
    var data, flag = false;

    it("ajax是否按时返回了正确结果", function(){
        runs(function(){
            $.post(
                url,
                {},
                function(data){
                    flag = true;
                    data = data.someAttr;
                }
            );
        });

        waitsFor(function(){  //flag为true或到2秒时执行  2秒内返回true则执行最后一个runs,到时未返回则本spec出错,返回第二个参数错误信息
            return flag;
        }, "ajax在指定时间2秒内未返回", 2000);

        runs(function(){  //直到waitsFor返回true时执行
            expect(data).toEqual("someThing");
        })
    });
});

注:it是一个spec,包含

runs(function)

waitsFor(function, message, millsec)

runs(function)

第一个runs里有一些异步的代码

waitsFor中的funciton若是在millsec内返回true,执行最后一个runs

       若是在millsec内不能返回true,spec不经过,显示错误信息message

原文代码:

 

e)        使用

在测试的页面里加入如下代码:

  <script type="text/javascript">

    (function() {
       var jasmineEnv = jasmine.getEnv();
       jasmineEnv.updateInterval = 1000;

       var trivialReporter = new jasmine.TrivialReporter();

       jasmineEnv.addReporter(trivialReporter);

       jasmineEnv.specFilter = function(spec) {
       	return trivialReporter.specFilter(spec);
       };

       var currentWindowOnload = window.onload;

       window.onload = function() {
        	if (currentWindowOnload) {
         		currentWindowOnload();
         	}
         	execJasmine();
       };

       function execJasmine() {
         	jasmineEnv.execute();
       }

    })();
  </script>

导入jasmine.css  jasmine.js  jasmine-html.js 

       src.js(源代码)  test.js(存放describes)

 

参考:http://www.weakweb.com/articles/255.html

       http://pivotal.github.com/jasmine/

下载:https://github.com/pivotal/jasmine/downloads

 

 

l  JsTestDriver

a)        简介

JsTestDriver是一个JavaScript单元测试工具,易于与持续构建系统相集成并可以在多个浏览器上运行测试轻松实现TDD风格的开发。当在项目中配置好JsTestDriver之后,如同junit测试java文件通常,JsTestDriver能够直接经过运行js文件来进行单元测试。JsTestDriver框架自己就是JAVA的jar包,须要在本地运行并监听一个端口。

b)       优势

能够一次测试多个浏览器,使用方法是在启动服务时能够将多个浏览器的路径做为参数传进去。能够在多台机器上的浏览器中运行,包括移动设备。

测试运行得很快,由于不须要将结果添加到DOM中呈现出来,它们可以同时在任意多的浏览器中运行,未修改的文件浏览器会从缓存提取。

不须要HTML配件文件,仅仅只需提供一个或多个脚本和测试脚本,测试运行器运行时会建立一个空文件。

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

有Eclipse和IntelliJ插件,能够很方便的在这两个IDE中进行测试,和JUnit很像。

支持其它测试框架,能够测试其它测试框架写的测试代码,好比有对应的插件能够将QUnit和Jasmine测试代码转换成JsTestDriver的测试代码。

c)        不足

不能在浏览器中测试,只能经过自动化工具或控制台运行。生成的结果不够直观。

安装使用稍微有点麻烦,依赖于JAVA环境。

d)       API

assert(msg, value) 

assertTrue(msg, value) 

assertFalse(msg, value) 

assertEquals(msg, expected, actual) 

assertNotEquals(msg, expected, actual) 

assertSame(msg, expected, actual) 

assertNotSame(msg, expected, actual) 

assertNull(msg, value) 

assertNotNull(msg, value) 

assertUndefined(msg, value) 

assertNotUndefined(msg, value) 

assertNaN(msg, number) 

assertNotNaN(msg, number) 

assertException(msg, callback, type) 

assertNoException(msg, callback) 

assertArray(msg, arrayLike) 

assertTypeOf(msg, type, object) 

assertBoolean(msg, value) 

assertFunction(msg, value) 

assertNumber(msg, value) 

assertObject(msg, value) 

assertString(msg, value) 

assertMatch(msg, pattern, string) 

assertNoMatch(msg, pattern, string) 

assertTagName(msg, tagName, element) 

assertClassName(msg, className, element) 

assertElementId(msg, id, element) 

assertInstanceOf(msg, constructor, object) 

assertNotInstanceOf(msg, constructor, object)

API按字面意思理解便可,不一一标注

e)        使用

前提:

安装java环境

下载JsTestDriver.jar

 

目录:

JsTestDriver.jar

jsTestDriver.conf        //配置文件,默认名称,若是用其它名称,须要指定config参数

src

----src.js

test

----test.js

 

jsTestDriver.conf:

 

src.js:

test.js:

像java的JUnit同样,测试方法名要以”test”开头,如:”testXXXX”

测试步骤:

  1. cmd 进入目录
  2. 运行命令”java –jar JsTestDriver.jar –port 9876”
  3. 打开页面http://localhost:9876,点击“捕获浏览器”。
  4. 新打开一个终端,运行命令”java –jar JsTestDriver.jar –tests all”,运行全部测试用例
  5. 也能够单独运行某一个用例,如运行命令” java –jar JsTestDriver.jar –tests addTest.testA”

 

安装:http://code.google.com/p/js-test-driver/wiki/UsingTheEclipsePlugin

下载:http://code.google.com/p/js-test-driver/

参考:《测试驱动的JavaScript开发》

      

l  FireUnit

a)        简介

FireUnit是一个基于Firebug的Javascript的单元测试框架。简单说来,FireUnit给Firebug增长了一个标签面板,并提供了一些简单的JavaScript API来记录和查看测试。

b)       优势

简单易用

c)        不足

功能很少,测试代码经常写在源码里,虽然能够实时地看到效果,但耦合太强,不易清理

只运行在Firefox下

d)       API

经常使用:

fireunit.ok(condition, message)   true/false

fireunit.compare(actual, expect, message)   是否相等

fireunit.reCompare(regexp, string, message)   字符串是否与正则式匹配

fireunit.testDone();   执行以上的测试,在Firebug的新标签test中显示结果

其它:

fireunit.runTests(“test2.html”, “test3.html”)   一块儿运行多个页面的测试(每一个文件都含有一些独立的测试)

fireunit.log(message)   打印log,但彷佛无论用

fireunit.id(id)   至关与document.getElementById

fireunit.click(element)   模拟触发element的click事件

fireunit.focus(element)   模拟触发element的focus事件

fireunit.mouseDown(element)   模拟触发element的mouseDown事件

       但看代码明明是click事件

       在FF下,只是执行了node.click();不会触发onmousedown,只会触发onclick。已验证

fireunit.value(element)   修改element的value

fireunit.key(element, key)   模拟触发element的key事件

       例:

       var input = document.getElementsByTagName(“input”)[0];

       fireunit.key(input, “a”);

没用过:

fireunit.browser

fireunit.test

fireunit.forceHTTP

fireunit.registerPathHandler

fireunit.panel

fireunit.privilege

 

 

e)        使用

在FF下装Firebug和FireUnit

直接在代码里写测试,就像console.log();

 

参考:https://github.com/jeresig/fireunit/wiki/internals

       http://hi.baidu.com/dearhwj/blog/item/1d1e0dbfc380f80219d81f35.html

 

4.      小结

QUnit框架简单方便,测试界面直观详细

Jasmine功能强大,风格也简单明了,符合前端开发者的编程习惯,推荐

JsTestDriver能够和QUnit等框架结合,能够同时测多个浏览器。但安装复杂,只能在控制台显示,不友好,不够清晰

FireUnit小巧灵活,加上Firebug的人气应该很受欢迎

若是须要进行自动化测试, 多了解一下Jasmine和JsTestDriver,本文未深刻。

 

5.      附注

JavaScript单元测试框架详细列表

相关文章
相关标签/搜索