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