爱淘FE

Posted by:
Mickey

每件事最后都会是好事,如果不是好事,说明还没到最后

1,889

javascript单元测试,Qunit和Jasmine

1.      JavaScript单元测试

单元测试关注的是验证一个模块或一段代码的执行效果是否和设计或预期一样。有些开发人员认为,编写测试用例浪费时间而宁愿去编写新的模块。然而,在处理大型应用程序时,单元测试实际上会节省时间;它能帮助开发人员跟踪问题并安全地更新代码。

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

随着单元测试的普及,尤其是敏捷开发的推动,涌现了许多优秀的JavaScript单元测试框架,如QUnit、Jasmine等。所有的这些框架基本上都能对Javascript代码进行很好的测试,当然UI部分的代码测试一样比较麻烦,但是我们可以通过精心构造我们的测试代码来测试部分UI代码。

 

2.      Qunit和Jasmine

首先引用infoQ对两个框架维护者采访时的原话作为开头:

InfoQ: 你的项目是做什么的,和其他JavaScript测试框架有什么不同?

Jörn Zaefferer : QUnit是一个JavaScript单元测试框架,主要用于在浏览器中运行单元测试。虽然这个项目从属于jQuery,但却不依赖于jQuery,也不依赖于浏览器DOM。因此你也可以在node.js或Rhino上使用。QUnit很容易学习,你只需在html页面中包含两个文件,不需要安装或者构建任何其他东西。最短的测试集只需要一个11行的html文件。

Davis Frank: Jasmine是一个 JavaScript测试框架,目的是将BDD风格引入JavaScript测试之中。至于区别嘛,我们的目标是BDD(相比标准的TDD),因此我们尽 力帮助开发人员编写比一般xUnit框架表达性更强,组织更好的代码。此外我们还力图减少依赖,这样你可以在node.js上使用Jasmine,也可以在浏览器或移动程序中使用。

InfoQ: 能大概说一下项目的架构么?主要的组件有哪些?

Jörn Zaefferer: QUnit的API非常直观: 调用test方法,传入测试的名字和回调函数,在回调函数中编写测试代码,其中包含一些断言。QUnit还提供一些选项来组织测试,例如含有setup和teardown的模块。在内部这些调用被映射为队列。根据执行环境的不同,QUnit会在window.load或其他事件发生时(或者由用户触发)开始执行测试。当某个测试需要异步执行时,会等到测试执行结束后再执行下一条。测试的执行顺序是变化的: 这是一个可选的功能,默认会根据上一次执行的结果重新排列执行顺序,但结果还是按照之前的顺序显示。如果你选择隐藏已经通过的测试,就可以重新运行所有失败的测试,无需等待将全部测试都运行一遍的时间,可以更快知道这些测试是否被修复。

Davis Frank: 我们尽量保持简洁,Jasmine的核心只有一个JavaScript文件,它提供了一种领域专用语言来编写测试。这种领域专用语言将测试组织为树状结构并执行。这个部分非常的简单。

在核心部分之上,还有一些用于在不同环境中显示测试报告的功能,以及方便管理源文件和测试文件的功能。Jasmine可以在Ruby on Rails或任何Web框架中使用。还有些人用它在node.js环境中做开发,任何使用JavaScript的地方,Jasmine都应该能够使用, 如果不能我们就要让它变成可能。

l   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:所有代码的执行时间

 

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

发表评论


back up ↑