Jasmine框架

上一篇 / 下一篇  2014-06-10 18:21:06 / 个人分类:JS单元测试


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);
    });

TAG:

 

评分:0

我来说两句