Jasmine中文指南

jasmine是一款非常流行的测试框架,不依赖任何别的库,语法简单,本文是2.1版本的api中文指南.

运行条件

  • npm install -g jasmine

  • jasmine JASMINE_CONFIG_PATH=jasmine.json

  • jasmine.json配置文件可以随意命名,内容格式如下

    {
      "spec_dir": "jasminetest",
      "spec_files": [
        "*.js"
      ]
    }
  • spec_dir代表要测试用例的目录,spec_files代表要测试的文件信息

一个简单的jasmine测试用例总是以describe开始的,它代表一组相似的测试用例,然后具体的测试用例以it开始,先看看下面简单的例子

describe(' test some code ', function(){
    var foo = 'foo';

    it(' expect foo equal foo ', function(){
        expect(foo).toEqual('foo');
    });

})

下面是jasmine的常用断言方法,在任何断言方法前面加上not,代表相反的意思.

toBe

类似于`===`
expect(true).toBe(true);

toEqual

比较变量字面量的值
expect({ foo: 'foo'}).toEqual( {foo: 'foo'} );

toMatch

匹配值与正则表达式
expect('foo').toMatch(/foo/);

toBeDefined

检验变量是否定义
var foo = {
    bar: 'foo'
};
expect(foo.bar).toBeDefined();

toBeNull

检验变量是否为`null`
var foo = null;
expect(foo).toBeNull();

toBeTruthy

检查变量值是否能转换成布尔型`真`值
expect({}).toBeTruthy();

toBeFalsy

检查变量值是否能转换成布尔型`假`值
expect('').toBeFalsy();

toContain

检查在数组中是否包含某个元素
expect([1,2,4]).toContain(1);

toBeLessThan

检查变量是否小于某个数字
expect(2).toBeLessThan(10);

toBeGreaterThan

检查变量是否大于某个数字或者变量
expect(2).toBeGreaterThan(1);

toBeCloseTo

比较两个数在保留几位小数位之后,是否相等,用于数字的精确比较
expect(3.1).toBeCloseTo(3, 0);

toThrow

检查一个函数是否会throw异常
expect(function(){ return a + 1;}).toThrow();  // true
expect(function(){ return a + 1;}).not.toThrow(); // false

toHaveBeenCalled

检查一个监听函数是否被调用过
  • 代码见下面的监听函数部分

toHaveBeenCalledWith

检查监听函数调用时的参数匹配信息
  • 代码见下面的监听函数部分

测试前后注入

  • beforeEach

    在describe里的每个it执行前调用

describe('test code inject', function(){
    var foo;

    // 每次给foo加1
    beforeEach(function(){
        foo += 1;
    });

    it('expect foo toBe 1 ', function(){
        expect(foo).toBe(1);
    });

    it('expect foo toEqual 2 ', function(){
        expect(foo).toEqual(2);
    });

});
  • afterEach

    在describe里的每个it执行后调用

describe('test code inject', function(){
    var foo;

    // 每次给foo加1
    beforeEach(function(){
        foo += 1;
    });

    // 重置变量foo
    afterEach(function(){
        foo = 0;
    });

    it('expect foo toBe 1 ', function(){
        expect(foo).toBe(1);
    });

    it('expect foo toEqual 1 ', function(){
        expect(foo).toEqual(1);
    });

});
  • beforeAll

    保证在describe里所有的it执行之前调用

describe('test beforeAll ', function(){
    var foo;

    // 只执行一次,保证在it执行之前执行
    beforeAll(function(){
        foo += 1;
    });


    it('expect foo toBe 1 ', function(){
        expect(foo).toBe(1);
    });

    it('expect foo toEqual 1 ', function(){
        expect(foo).toEqual(1);
    });

});
  • afterAll

    保证在describe里所有的it执行完成之后调用

describe('test beforeAll ', function(){
    var foo;

    // 只执行一次,保证在it执行之前执行
    beforeAll(function(){
        foo += 1;
    });

    afterAll(function(){
        foo = 0;
    });


    it('expect foo toBe 1 ', function(){
        expect(foo).toBe(1);
        foo += 1;
    });

    it('expect foo toEqual 2 ', function(){
        expect(foo).toEqual(2);
    });

    // foo 0

});

this关键字

  • describe里的每个beforeEach,it,afterEachthis都是相同的.
describe('test this context', function(){


    beforeEach(function(){
        this.bar = 'yicai';
    });

    afterEach(function(){
        console.log(this.bar); // yicai
    });

    it('expect this.bar yicai ', function(){
        console.log(this.bar); // yicai
    });

    it('expect this.bar yicai ', function(){
        console.log(this.bar); // yicai
    });

});

嵌套describe

  • 不同层次的it执行时,会按从外到内依次执行beforeEach,每个it执行结束时,会按从内到外依次执行afterEach.
describe('test nesting describe ', function(){

    var foo = 0;

    beforeEach(function(){
        foo += 1;
    });
    afterEach(function(){
        foo = 0;
        console.log('second invoke parent afterEach');
    })

    it('expect foo toBe 1', function(){
        expect(foo).toBe(1);
    });

    describe('child describe ', function(){
        beforeEach(function(){
            foo += 1;
        })
        afterEach(function(){
            console.log('first invoke child afterEach');
        })

        it('expect foo toEqual 2', function(){
            expect(foo).toEqual(2);
        });
    })
});

禁用describe与it

  • 当在describeit前面加上x前缀时,可以禁掉当前describe和it测试

  • 当使用xit或者it里不包含函数体时,测试结果会显示挂起spec字样

spy监视函数执行

  • spyOn方法可以添加对某个对象下的函数执行情况的监控
describe('test spy ', function(){
    var  spyobj, bar = null;
    beforeEach(function(){
        spyobj = {
            setBar: function(val){
                bar = val;
            }
        }
        spyOn(spyobj, 'setBar');
        spyobj.setBar('123');
        spyobj.setBar('1', '2');
    });

    it('check spyobj invoke track', function(){
        // 检查是否监听函数是否调用过
        expect(spyobj.setBar).toHaveBeenCalled();
        // 检查监听函数参数调用情况
        expect(spyobj.setBar).toHaveBeenCalledWith('123');
        expect(spyobj.setBar).toHaveBeenCalledWith('1', '2');
        // bar变量值默认是不会保存的
        expect(bar).toBeNull();
    })
});
  • and.callThrough方法可以让监听的方法返回值保留下来
describe('test spy ', function(){
    var  spyobj, bar = null;
    beforeEach(function(){
        spyobj = {
            setBar: function(val){
                bar = val;
            }
        }
        spyOn(spyobj, 'setBar').and.callThrough();
        spyobj.setBar('123');
        spyobj.setBar('1', '2');
    });

    it('check spyobj invoke track', function(){
        // bar变量此次保存了下来
        expect(bar).toEqual('1');
    })
});
  • and.returnValue方法可以指定监听的方法返回值
describe('test spy ', function(){
    var  spyobj, bar = null, foo;
    beforeEach(function(){
        spyobj = {
            setBar: function(val){
                bar = val;
            },
            getBar: function(){
                return bar;
            }
        }
        spyOn(spyobj, "getBar").and.returnValue('1');
        spyobj.setBar('123');
        foo = spyobj.getBar();
    });

    it('check spyobj invoke track', function(){

        expect(bar).toEqual('123');
        // returnValue改变了原先的setBar方法设置的值
        expect(foo).toEqual('1');
    })
});
  • and.callFake方法可以伪造监听的方法返回值,通过一个自定义函数
describe('test spy ', function(){
    var  spyobj, bar = null, foo;
    beforeEach(function(){
        spyobj = {
            setBar: function(val){
                bar = val;
            },
            getBar: function(){
                return bar;
            }
        }
        spyOn(spyobj, "getBar").and.callFake(function(){
            return 'yicai';
        });
        spyobj.setBar('123');
        foo = spyobj.getBar();
    });

    it('check spyobj invoke track', function(){

        expect(bar).toEqual('123');
        // callFake改变了原先的setBar方法设置的值
        expect(foo).toEqual('yicai');
    })
});
  • and.throwError方法可以让监听方法执行之后返回一个错误信息,可以通过toThrowError来适配
describe('test spy ', function(){
    var  spyobj, bar = null;
    beforeEach(function(){
        spyobj = {
            setBar: function(val){
                bar = val;
            },
            getBar: function(){
                return bar;
            }
        }
        spyOn(spyobj, "setBar").and.throwError('error');
    });

    it('check spyobj invoke track', function(){
        expect(function(){
            spyobj.setBar();
        }).toThrowError('error');
    })
});
  • and.stub方法可以还原监听方法的返回值
describe('test spy ', function(){
    var  spyobj, bar = null;
    beforeEach(function(){
        spyobj = {
            setBar: function(val){
                bar = val;
            },
            getBar: function(){
                return bar;
            }
        }
        spyOn(spyobj, "setBar").and.callThrough();
    });

    it('check spyobj invoke track', function(){
        spyobj.setBar('123');
        expect(bar).toEqual('123');
        spyobj.setBar.and.stub();
        bar = null;
        spyobj.setBar('123');
        expect(bar).toBe(null);
    })
});
  • calls这里包含很多监听过程中的属性信息

    • .calls.any(),一次都没调用,则返回false,否则返回true
    • .calls.count(),返回监听函数调用的次数
    • .calls.argsFor(index),返回监听函数调用过程中传递的所有参数信息,index代表调用的索引数
    • .calls.allArgs(),返回监听函数调用过程中的所以参数信息,是一个数组,每一项代表一次调用传参信息
    • .calls.all(),返回监听函数调用过程中的所有信息,除了参数信息还包含this上下文信息
    • .calls.mostRecent(),返回监听函数最后一次调用的相关信息,除了参数还包含this上下文信息
    • .calls.first(),返回第一次调用监听函数的相关信息,除了参数还包含this上下文信息
    • .calls.reset(),清除监听函数调用信息,.calls.any()将返回false

下面以一个例子来说明,在访问监听函数上的calls属性时的信息

describe('test spy ', function(){
    var  spyobj, bar = null;
    beforeEach(function(){
        spyobj = {
            setBar: function(val){
                bar = val;
            },
            getBar: function(){
                return bar;
            }
        }
        spyOn(spyobj, "setBar");
    });

    it('check spyobj invoke track', function(){

        // 监听函数没调用过,则返回false
        expect(spyobj.setBar.calls.any()).toBe(false);

        spyobj.setBar('1');
        spyobj.setBar('2', '4');
        // 上面调用了2次
        expect(spyobj.setBar.calls.count()).toEqual(2);

        // 分别获取上面调用两次时的入参信息,索引就是调用顺序
        expect(spyobj.setBar.calls.argsFor(0)).toEqual(['1']);
        expect(spyobj.setBar.calls.argsFor(1)).toEqual(['2', '4']);

        // 获取所有调用时的入参信息
        expect(spyobj.setBar.calls.allArgs()).toEqual([ ['1'], ['2', '4'] ]);

        // 获取所有调用信息,包括this上下文信息
        expect(spyobj.setBar.calls.all()).toEqual([
        {
            object: spyobj, args: ['1'], returnValue: undefined
        },
        {
            object: spyobj, args: ['2','4'], returnValue: undefined
        }
        ]);

        // 获取最近一次调用的信息
        expect(spyobj.setBar.calls.mostRecent()).toEqual({object: spyobj, args: ['2', '4'], returnValue: undefined});

        // 获取第一次调用的信息
        expect(spyobj.setBar.calls.first()).toEqual({object: spyobj, args: ['1'], returnValue: undefined});

        // 清除监听函数调用信息
        spyobj.setBar.calls.reset();

        expect(spyobj.setBar.calls.any()).toBe(false);
    })
});

注意,当在calls对象上调用all(),mostRecent(),first(),返回的object属性指向的是this上下文信息

  • createSpy,可以创建一个命名的监听函数
describe('test spy ', function(){
    var  spyobj, bar = null;
    beforeEach(function(){
        spyobj = jasmine.createSpy('spyobj');
        spyobj('1', '2');
    });

    it('check spyobj invoke track', function(){

        expect(spyobj.and.identity()).toEqual('spyobj');

        expect(spyobj.calls.any()).toBe(true);

        expect(spyobj.calls.count()).toEqual(1);

        expect(spyobj).toHaveBeenCalledWith('1', '2');

        expect(spyobj.calls.mostRecent().args[0]).toEqual('1');
    })
});
  • createSpyObj,可以批量创建监听函数
describe('test spy ', function(){
    var  spyobj, bar = null;
    beforeEach(function(){
        spyobj = jasmine.createSpyObj('spyobj', ['play', 'pause', 'stop', 'rewind']);
        spyobj.play();
        spyobj.pause('1');
    });

    it('check spyobj invoke track', function(){

        expect(spyobj.rewind.and.identity()).toEqual('spyobj.rewind');

        expect(spyobj.play.calls.any()).toBe(true);

        expect(spyobj.stop.calls.any()).toBe(false);

        expect(spyobj.pause.calls.count()).toEqual(1);

        expect(spyobj.pause.calls.mostRecent().args[0]).toEqual('1');
    })
});

jasmine.any

检验变量是否匹配相关类型
describe("jasmine.any", function() {
  it("matches any value", function() {
    expect({}).toEqual(jasmine.any(Object));
    expect(12).toEqual(jasmine.any(Number));
  });

  describe("when used with a spy", function() {
    it("is useful for comparing arguments", function() {
      var foo = jasmine.createSpy('foo');
      foo(12, function() {
        return true;
      });

      expect(foo).toHaveBeenCalledWith(jasmine.any(Number), jasmine.any(Function));
    });
  });
});

jasmine.objectContaining

检验对象是否包含某个`key/value`
describe("jasmine.objectContaining", function() {
  var foo;

  beforeEach(function() {
    foo = {
      a: 1,
      b: 2,
      bar: "baz"
    };
  });

  it("matches objects with the expect key/value pairs", function() {
    expect(foo).toEqual(jasmine.objectContaining({
      bar: "baz"
    }));
    expect(foo).not.toEqual(jasmine.objectContaining({
      c: 37
    }));
  });

  describe("when used with a spy", function() {
    it("is useful for comparing arguments", function() {
      var callback = jasmine.createSpy('callback');

      callback({
        bar: "baz"
      });

      expect(callback).toHaveBeenCalledWith(jasmine.objectContaining({
        bar: "baz"
      }));
      expect(callback).not.toHaveBeenCalledWith(jasmine.objectContaining({
        c: 37
      }));
    });
  });
});

jasmine.clock

jasmine的时钟
  • jasmine.clock().install(), 启动时钟控制

  • jasmine.clock().uninstall(), 停止时钟控制

  • jasmine.clock().tick, 让时钟往前走多少秒

  • jasmine.clock().mockDate, 可以根据传入的date来设置当前时间

下面以一个完整的例子来说明

describe('test jasmine.clock', function(){
    var timecallback;

    beforeEach(function(){
        timecallback = jasmine.createSpy('timecallback');
        jasmine.clock().install();
    });

    afterEach(function(){
        jasmine.clock().uninstall();
    })

    it('mock setTimeout clock ', function(){
        setTimeout(function(){
            timecallback();
        }, 100);
        expect(timecallback).not.toHaveBeenCalled();
        jasmine.clock().tick(101);
        expect(timecallback).toHaveBeenCalled();
    })

    it('mock setInterval clock ', function(){
        setInterval(function(){
            timecallback();
        }, 100);
        expect(timecallback).not.toHaveBeenCalled();
        jasmine.clock().tick(101);
        expect(timecallback.calls.count()).toEqual(1);
        jasmine.clock().tick(50);
        expect(timecallback.calls.count()).toEqual(1);
        jasmine.clock().tick(50);
        expect(timecallback.calls.count()).toEqual(2);
    })

    it('mock date clock ', function(){
        var baseTime = new Date(2013, 9, 23);
        jasmine.clock().mockDate(baseTime);
        jasmine.clock().tick(50);
        expect(new Date().getTime()).toEqual(baseTime.getTime() + 50);
    })

})

jasmine异步支持

  • beforeEachit,包装的函数传入done参数,只有当done函数执行完成之后,beforeEach, it才算执行完成

  • jasmine.DEFAULT_TIMEOUT_INTERVAL, 默认是5秒之后就超时,可以修改这个超时时间

下面以一个完整的例子来说jasmine中异步操作

describe('test asynchonous ', function(){
    var value = 0, originalTimeout;

    beforeEach(function(done){
        originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
        // 设置jasmine超时时间为10秒
        jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;
        setTimeout(function(){
            value += 1;
            // 只有执行done函数,后面的it才会执行
            done();
        }, 200);
    });

    afterEach(function(){
        // 还原jasmine超时时间
        jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
    })

    it('expect value toEqual 1', function(done){
        setTimeout(function(){
            expect(value).toEqual(1);
            // 只有执行这个,后面的it才会执行
            done();
        }, 9000);
    });

    it('until above spec complete ', function(){
        expect(value).toBe(2);
    });


})

相关链接

Jasmine 2.1 英文api文档


上一篇:[译]ng指令中的compile与link函数解析


下一篇:从d2来看前端的未来