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(); // trueexpect(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
,afterEach
的this
都是相同的.
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
-
当在
describe
和it
前面加上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异步支持
-
beforeEach
,it
,包装的函数传入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); });})