Jasmine入门(下)
上一篇 Jasmine入门(上) 介绍了Jasmine以及一些基本的用法,本篇我们继续研究Jasmine的其他一些特性及其用法(注:本篇中的例子均来自于官方文档)。
Spy
Spy用来追踪函数的调用历史信息(是否被调用、调用参数列表、被请求次数等)。Spy仅存在于定义它的describe和it方法块中,并且每次在spec执行完之后被销毁。
示例1:
(function(){
describe("A spy", function() {
var foo, bar = null;
beforeEach(function() {
foo = {
setBar: function(value) {
bar = value;
}
};
spyOn(foo, 'setBar'); // 在foo对象上添加spy
// 此时调用foo对象上的方法,均为模拟调用,因此不会执行实际的代码
foo.setBar(123); // 调用foo的setBar方法
foo.setBar(456, 'another param');
});
it("tracks that the spy was called", function() {
expect(foo.setBar).toHaveBeenCalled(); //判断foo的setBar是否被调用
});
it("tracks all the arguments of its calls", function() {
expect(foo.setBar).toHaveBeenCalledWith(123); //判断被调用时的参数
expect(foo.setBar).toHaveBeenCalledWith(456, 'another param');
});
it("stops all execution on a function", function() {
expect(bar).toBeNull(); // 由于是模拟调用,因此bar值并没有改变
});
});
})();
从示例1中看到,当在一个对象上使用spyOn方法后即可模拟调用对象上的函数,此时对所有函数的调用是不会执行实际代码的。示例1中包含了两个Spy常用的expect:
toHaveBeenCalled: 函数是否被调用
toHaveBeenCalledWith: 调用函数时的参数
and.callThrough()
那如果说我们想在使用Spy的同时也希望执行实际的代码呢?
示例2:
(function(){
describe("A spy, when configured to call through", function() {
var foo, bar, fetchedBar;
beforeEach(function() {
foo = {
setBar: function(value) {
bar = value;
},
getBar: function() {
return bar;
}
};
spyOn(foo, 'getBar').and.callThrough(); // 与示例1中不同之处在于使用了callThrough,这将时所有的函数调用为真实的执行
//spyOn(foo, 'getBar'); // 可以使用示例1中的模拟方式,看看测试集执行的结果
foo.setBar(123);
fetchedBar = foo.getBar();
});
it("tracks that the spy was called", function() {
expect(foo.getBar).toHaveBeenCalled();
});
it("should not effect other functions", function() {
expect(bar).toEqual(123); // 由于是真实调用,因此bar有了真实的值
});
it("when called returns the requested value", function() {
expect(fetchedBar).toEqual(123); // 由于是真实调用,fetchedBar也有了真实的值
});
});
})();
通过在使用spyOn后面增加了链式调用and.CallThrough(),这将告诉Jasmine我们除了要完成对函数调用的跟踪,同时也需要执行实际的代码。
and.returnValue()
由于Spy是模拟函数的调用,因此我们也可以强制指定函数的返回值。
示例3:
(function(){
describe("A spy, when configured to fake a return value", function() {
var foo, bar, fetchedBar;
beforeEach(function() {
foo = {
setBar: function(value) {
bar = value;
},
getBar: function() {
return bar;
}
};
spyOn(foo, "getBar").and.returnValue(745); // 这将指定getBar方法返回值为745
foo.setBar(123);
fetchedBar = foo.getBar();
});
it("tracks that the spy was called", function() {
expect(foo.getBar).toHaveBeenCalled();
});
it("should not effect other functions", function() {
expect(bar).toEqual(123);
});
it("when called returns the requested value", function() {
expect(fetchedBar).toEqual(745);
});
});
})();
如果被调用的函数是通过从其他函数获取某些值,我们通过使用returnValue模拟函数的返回值。这样做的好处是可以有效的隔离依赖,使测试流程变得更简单。
and.callFake()
与returnValue相似,callFake则更进一步,直接通过指定一个假的自定义函数来执行。这种方式比returnValue更灵活,我们可以任意捏造一个函数来达到我们的测试要求。
示例4:
(function(){
describe("A spy, when configured with an alternate implementation", function() {
var foo, bar, fetchedBar;
beforeEach(function() {
foo = {
setBar: function(value) {
bar = value;
},
getBar: function() {
return bar;
}
};
spyOn(foo, "getBar").and.callFake(function() {
return 1001;
});
foo.setBar(123);
fetchedBar = foo.getBar();
});
it("tracks that the spy was called", function() {
expect(foo.getBar).toHaveBeenCalled();
});
it("should not effect other functions", function() {
expect(bar).toEqual(123);
});
it("when called returns the requested value", function() {
expect(fetchedBar).toEqual(1001);
});
});
})();
and.throwError()
throwError便于我们模拟异常的抛出。
(function(){
describe("A spy, when configured to throw an error", function() {
var foo, bar;
beforeEach(function() {
foo = {
setBar: function(value) {
bar = value;
}
};
spyOn(foo, "setBar").and.throwError("quux");
});
it("throws the value", function() {
expect(function() {
foo.setBar(123)
}).toThrowError("quux");
});
});
})();
and.stub
示例5:
(function(){
describe("A spy", function() {
var foo, bar = null;
beforeEach(function() {
foo = {
setBar: function(value) {
bar = value;
},
getBar: function(){
return bar;
}
};
spyOn(foo, 'setBar').and.callThrough(); // 标记1
spyOn(foo, 'getBar').and.returnValue(999); // 标记2
});
it("can call through and then stub in the same spec", function() {
foo.setBar(123);
expect(bar).toEqual(123);
var getValue = foo.getBar();
expect(getValue).toEqual(999);
foo.setBar.and.stub(); // 相当于'标记1'中的代码变为了spyOn(foo, 'setBar')
foo.getBar.and.stub(); // 相当于'标记2'中的代码变为了spyOn(foo, 'getBar')
bar = null;
foo.setBar(123);
expect(bar).toBe(null);
expect(foo.setBar).toHaveBeenCalled(); // 函数调用追踪并没有被重置
getValue = foo.getBar();
expect(getValue).toEqual(undefined);
expect(foo.getBar).toHaveBeenCalled(); // 函数调用追踪并没有被重置
});
});
})();
其他追踪属性:
calls:对于被Spy的函数的调用,都可以在calls属性中跟踪。
- .calls.any(): 被Spy的函数一旦被调用过,则返回true,否则为false;
- .calls.count(): 返回被Spy的函数的被调用次数;
- .calls.argsFor(index): 返回被Spy的函数的调用参数,以index来指定参数;
- .calls.allArgs():返回被Spy的函数的所有调用参数;
- .calls.all(): 返回calls的上下文,这将返回当前calls的整个实例数据;
- .calls.mostRecent(): 返回calls中追踪的最近一次的请求数据;
- .calls.first(): 返回calls中追踪的第一次请求的数据;
- .object: 当调用all(),mostRecent(),first()方法时,返回对象的object属性返回的是当前上下文对象;
- .calls.reset(): 重置Spy的所有追踪数据;
示例6:
(function(){
describe("A spy", function() {
var foo, bar = null;
beforeEach(function() {
foo = {
setBar: function(value) {
bar = value;
}
};
spyOn(foo, 'setBar');
});
it("tracks if it was called at all", function() {
expect(foo.setBar.calls.any()).toEqual(false);
foo.setBar();
expect(foo.setBar.calls.any()).toEqual(true);
});
it("tracks the number of times it was called", function() {
expect(foo.setBar.calls.count()).toEqual(0);
foo.setBar();
foo.setBar();
expect(foo.setBar.calls.count()).toEqual(2);
});
it("tracks the arguments of each call", function() {
foo.setBar(123);
foo.setBar(456, "baz");
expect(foo.setBar.calls.argsFor(0)).toEqual([123]);
expect(foo.setBar.calls.argsFor(1)).toEqual([456, "baz"]);
});
it("tracks the arguments of all calls", function() {
foo.setBar(123);
foo.setBar(456, "baz");
expect(foo.setBar.calls.allArgs()).toEqual([[123],[456, "baz"]]);
});
it("can provide the context and arguments to all calls", function() {
foo.setBar(123);
expect(foo.setBar.calls.all()).toEqual([{object: foo, args: [123], returnValue: undefined}]);
});
it("has a shortcut to the most recent call", function() {
foo.setBar(123);
foo.setBar(456, "baz");
expect(foo.setBar.calls.mostRecent()).toEqual({object: foo, args: [456, "baz"], returnValue: undefined});
});
it("has a shortcut to the first call", function() {
foo.setBar(123);
foo.setBar(456, "baz");
expect(foo.setBar.calls.first()).toEqual({object: foo, args: [123], returnValue: undefined});
});
it("tracks the context", function() {
var spy = jasmine.createSpy('spy');
var baz = {
fn: spy
};
var quux = {
fn: spy
};
baz.fn(123);
quux.fn(456);
expect(spy.calls.first().object).toBe(baz);
expect(spy.calls.mostRecent().object).toBe(quux);
});
it("can be reset", function() {
foo.setBar(123);
foo.setBar(456, "baz");
expect(foo.setBar.calls.any()).toBe(true);
foo.setBar.calls.reset();
expect(foo.setBar.calls.any()).toBe(false);
});
});
})();
createSpy
假如没有函数可以追踪,我们可以自己创建一个空的Spy。创建后的Spy功能与其他的Spy一样:跟踪调用、参数等,但该Spy没有实际的代码实现,这种方式经常会用在对JavaScript中的对象的测试。
示例7:
(function(){
describe("A spy, when created manually", function() {
var whatAmI;
beforeEach(function() {
whatAmI = jasmine.createSpy('whatAmI');
whatAmI("I", "am", "a", "spy");
});
it("is named, which helps in error reporting", function() {
expect(whatAmI.and.identity()).toEqual('whatAmI');
});
it("tracks that the spy was called", function() {
expect(whatAmI).toHaveBeenCalled();
});
it("tracks its number of calls", function() {
expect(whatAmI.calls.count()).toEqual(1);
});
it("tracks all the arguments of its calls", function() {
expect(whatAmI).toHaveBeenCalledWith("I", "am", "a", "spy");
});
it("allows access to the most recent call", function() {
expect(whatAmI.calls.mostRecent().args[0]).toEqual("I");
});
});
})();
createSpyObj
如果需要spy模拟多个函数调用,可以向jasmine.createSpyObj中传入一个字符串数组,它将返回一个对象,你所传入的所有字符串都将对应一个属性,每个属性即为一个Spy。
示例8:
(function(){
describe("Multiple spies, when created manually", function() {
var tape;
beforeEach(function() {
tape = jasmine.createSpyObj('tape', ['play', 'pause', 'stop', 'rewind']);
tape.play();
tape.pause();
tape.rewind(0);
});
it("creates spies for each requested function", function() {
expect(tape.play).toBeDefined();
expect(tape.pause).toBeDefined();
expect(tape.stop).toBeDefined();
expect(tape.rewind).toBeDefined();
});
it("tracks that the spies were called", function() {
expect(tape.play).toHaveBeenCalled();
expect(tape.pause).toHaveBeenCalled();
expect(tape.rewind).toHaveBeenCalled();
expect(tape.stop).not.toHaveBeenCalled();
});
it("tracks all the arguments of its calls", function() {
expect(tape.rewind).toHaveBeenCalledWith(0);
});
});
})();
关于createSpy和createSpyObj,读到这里大家可能很难理解其真正的应用场景。不过没关系,后续的例子中也包含了其用法,大家应该能慢慢理解如何运用它们。
其他匹配方式
jasmine.any
jasmine.any方法以构造器或者类名作为参数,Jasmine将判断期望值和真实值的构造器是否相同,若相同则返回true。
示例9:
(function(){
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.anything
jamine.anything判断只要不是null或undefined的值,若不是则返回true。
示例10:
(function(){
describe("jasmine.anything", function() {
it("matches anything", function() {
expect(1).toEqual(jasmine.anything());
});
describe("when used with a spy", function() {
it("is useful when the argument can be ignored", function() {
var foo = jasmine.createSpy('foo');
foo(12, function() {
return false;
});
expect(foo).toHaveBeenCalledWith(12, jasmine.anything());
});
});
});
})();
jasmine.objectContaining
jasmine.objectContaining用来判断对象中是否存在指定的键值属性对。
示例11:
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.arrayContaining
jasmine.arrayContaining可以用来判断数组中是否有期望的值。
示例12:
(function(){
describe("jasmine.arrayContaining", function() {
var foo;
beforeEach(function() {
foo = [1, 2, 3, 4];
});
it("matches arrays with some of the values", function() {
expect(foo).toEqual(jasmine.arrayContaining([3, 1])); // 直接在期望值中使用jasmine.arrayContaining达到目的
expect(foo).not.toEqual(jasmine.arrayContaining([6]));
});
describe("when used with a spy", function() {
it("is useful when comparing arguments", function() {
var callback = jasmine.createSpy('callback'); // 创建一个空的Spy
callback([1, 2, 3, 4]); // 将数组内容作为参数传入Spy中
expect(callback).toHaveBeenCalledWith(jasmine.arrayContaining([4, 2, 3]));
expect(callback).not.toHaveBeenCalledWith(jasmine.arrayContaining([5, 2]));
});
});
});
})();
jasmine.stringMatching
jasmine.stringMatching用来模糊匹配字符串,在jasmine.stringMatching中也可以使用正则表达式进行匹配,使用起来非常灵活。
示例13:
describe('jasmine.stringMatching', function() {
it("matches as a regexp", function() {
expect({foo: 'bar'}).toEqual({foo: jasmine.stringMatching(/^bar$/)});
expect({foo: 'foobarbaz'}).toEqual({foo: jasmine.stringMatching('bar')});
});
describe("when used with a spy", function() {
it("is useful for comparing arguments", function() {
var callback = jasmine.createSpy('callback');
callback('foobarbaz');
expect(callback).toHaveBeenCalledWith(jasmine.stringMatching('bar'));
expect(callback).not.toHaveBeenCalledWith(jasmine.stringMatching(/^bar$/));
});
});
});
不规则匹配(自定义匹配):asymmetricMatch
某些场景下,我们希望能按照自己设计的规则进行匹配,此时我们可以自定义一个对象,该对象只要包含一个名为asymmetricMatch的方法即可。
示例14:
describe("custom asymmetry", function() {
var tester = {
asymmetricMatch: function(actual) {
var secondValue = actual.split(',')[1];
return secondValue === 'bar';
}
};
it("dives in deep", function() {
expect("foo,bar,baz,quux").toEqual(tester);
});
describe("when used with a spy", function() {
it("is useful for comparing arguments", function() {
var callback = jasmine.createSpy('callback');
callback('foo,bar,baz');
expect(callback).toHaveBeenCalledWith(tester);
});
});
});
注:示例中的asymmetricMatch方法使我们判断字符串以','分割之后,index为1的内容为'bar'。
Jasmine Clock
Jasmine中可以使用jasmine.clock()方法来模拟操纵时间。
要想使用jasmine.clock(),先调用jasmine.clock().install告诉Jasmine你想要在spec或者suite操作时间,当你不需要使用时,务必调用jasmine.clock().uninstall来恢复时间状态。
示例15:
(function(){
describe("Manually ticking the Jasmine Clock", function() {
var timerCallback;
beforeEach(function() {
timerCallback = jasmine.createSpy("timerCallback");
jasmine.clock().install();
});
afterEach(function() {
jasmine.clock().uninstall();
});
it("causes a timeout to be called synchronously", function() {
setTimeout(function() {
timerCallback();
}, 100);
expect(timerCallback).not.toHaveBeenCalled();
jasmine.clock().tick(101);
expect(timerCallback).toHaveBeenCalled();
});
it("causes an interval to be called synchronously", function() {
setInterval(function() {
timerCallback();
}, 100);
expect(timerCallback).not.toHaveBeenCalled();
jasmine.clock().tick(101);
expect(timerCallback.calls.count()).toEqual(1);
jasmine.clock().tick(50);
expect(timerCallback.calls.count()).toEqual(1);
jasmine.clock().tick(50);
expect(timerCallback.calls.count()).toEqual(2);
});
describe("Mocking the Date object", function(){
it("mocks the Date object and sets it to a given time", 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.clock().tick(milliseconds)来控制时间前进,本例中出现了三种时间控制方式:
- setTimeout: 定期执行一次,当jasmine.clock().tick()的时间超过了timeout设置的时间时触发
- setInterval: 定期循环执行,每当jasmine.clock().tick()的时间超过了timeout设置的时间时触发
- mockDate: 模拟一个指定日期(当不提供基准时间参数时,以当前时间为基准时间)
异步支持
Jasmine可以支持spec中执行异步操作,当调用beforeEach, it和afterEach时,函数可以包含一个可选参数done,当spec执行完毕之后,调用done通知Jasmine异步操作已执行完毕。
示例16:
(function(){
describe("Asynchronous specs", function() {
var value;
beforeEach(function(done) {
setTimeout(function() {
value = 0;
done();
}, 1);
});
// 在上面beforeEach的done()被执行之前,这个测试用例不会被执行
it("should support async execution of test preparation and expectations", function(done) {
value++;
expect(value).toBeGreaterThan(0);
done(); // 执行完done()之后,该测试用例真正执行完成
});
// Jasmine异步执行超时时间默认为5秒,超过后将报错
describe("long asynchronous specs", function() {
// 如果要调整指定用例的默认的超时时间,可以在beforeEach,it和afterEach中传入一个时间参数
beforeEach(function(done) {
// setTimeout(function(){}, 2000); // 可以试试如果该方法执行超过1秒时js会报错
done();
}, 1000);
it("takes a long time", function(done) {
setTimeout(function() {
done();
}, 9000);
}, 10000);
afterEach(function(done) {
done();
}, 1000);
});
});
})();
关于用法在代码中已经加了注释,另外补充一点,如果需要设置全局的默认超时时间,可以设置jasmine.DEFAULT_TIMEOUT_INTERVAL的值。
当异步执行时间超过设置的执行超时时间js将会报错:

至此为止,我们已经了解了Jasmine的所有用法,Jasmine的基本语法并不复杂,但是想要运用熟练还是需要在实际项目中慢慢实践。
参考资料
Jasmine官方:http://jasmine.github.io/2.3/introduction.html
KeenWon:http://keenwon.com/1218.html
Jasmine入门(下)的更多相关文章
- Jasmine入门(上)
什么是Jasmine Jasmine是一个Javascript的BDD(Behavior-Driven Development)测试框架,不依赖任何其他框架. 如何使用Jasmine 从Github上 ...
- Jasmine入门(结合示例讲解)
参考: http://www.cnblogs.com/wushangjue/p/4541209.html http://keenwon.com/1191.html http://jasmine.git ...
- jasmine入门
本文来自http://blog.fens.me/nodejs-jasmine-bdd 粉丝日志 张丹 前言TDD(Test Driven Development)测试驱动开发,是敏捷开发中提出的最 ...
- Python 装饰器入门(下)
继续上次的进度:https://www.cnblogs.com/flashBoxer/p/9847521.html 正文: 装饰类 在类中有两种不通的方式使用装饰器,第一个和我们之前做过的函数非常相似 ...
- Django入门(下)
一.创建APP 在每一个django项目中可以包含多个APP,相当于一个大型项目中的分系统.子模块.功能部件等.互相之间比较独立,但也有联系. 在pycharm下方的Terminal终端中输入命令: ...
- 网易云课堂_C++程序设计入门(下)_第10单元:月映千江未减明 – 模板_第10单元 - 单元作业:OJ编程 - 创建数组类模板
第10单元 - 单元作业:OJ编程 - 创建数组类模板 查看帮助 返回 温馨提示: 1.本次作业属于Online Judge题目,提交后由系统即时判分. 2.学生可以在作业截止时间之前不限次数提 ...
- 网易云课堂_C++程序设计入门(下)_第9单元:白公曾咏牡丹芳,一种鲜妍独“异常”_第9单元 - 作业5:OJ编程 - 使用异常进行图形类的错误处理
第9单元 - 作业5:OJ编程 - 使用异常进行图形类的错误处理 查看帮助 返回 温馨提示: 1.本次作业属于Online Judge题目,提交后由系统即时判分. 2.学生可以在作业截止时间之前 ...
- 网易云课堂_C++程序设计入门(下)_第7单元:出入虽同趣,所向各有宜 – 文件输入和输出_第7单元 - 作业2:编程互评
第7单元 - 作业2:编程互评 查看帮助 返回 提交作业(剩余10天) 完成并提交作业 作业批改 互评训练 互评作业 自评作业 成绩公布 查看成绩 由于在线编程不支持 ...
- Pytorch入门下 —— 其他
本节内容参照小土堆的pytorch入门视频教程. 现有模型使用和修改 pytorch框架提供了很多现有模型,其中torchvision.models包中有很多关于视觉(图像)领域的模型,如下图: 下面 ...
随机推荐
- 2018.09.26 bzoj5221: [Lydsy2017省队十连测]偏题(数学推导+矩阵快速幂)
传送门 由于没有考虑n<=1的情况T了很久啊. 这题很有意思啊. 考试的时候根本不会,骗了30分走人. 实际上变一个形就可以了. 推导过程有点繁杂. 直接粘题解上的请谅解. 不得不说这个推导很妙 ...
- 2018.09.23 bzoj3143: [Hnoi2013]游走(dp+高斯消元)
传送门 显然只需要求出所有边被经过的期望次数,然后贪心把边权小的边定城大的编号. 所以如何求出所有边被经过的期望次数? 显然这只跟边连接的两个点有关. 于是我们只需要求出两个点被经过的期望次数. 对于 ...
- 2018.06.29 洛谷P1505 [国家集训队]旅游(树链剖分)
旅游 题目描述 Ray 乐忠于旅游,这次他来到了T 城.T 城是一个水上城市,一共有 N 个景点,有些景点之间会用一座桥连接.为了方便游客到达每个景点但又为了节约成本,T 城的任意两个景点之间有且只有 ...
- python小练习--属性
箭头这个作业: class Box:#定义一个类名为Box,类名后不必有括号,类包含类属性和类方法,这个类没有定义类属性 '''这是一个计算体积的类'''#这是这个类的__doc__属性,执行类后就可 ...
- Asp.net mvc验证用户登录之Forms实现
这里我们采用asp.net mvc 自带的AuthorizeAttribute过滤器验证用户的身份,也可以使用自定义过滤器,步骤都是一样. 第一步:创建asp.net mvc项目, 在项目的App_S ...
- 从hbase到hive,以及sqoop转到mysql解析
https://blog.csdn.net/qq_33689414/article/details/80328665 hive关联hbase的配置文件 hive和hbase同步https://cwik ...
- 安装Tomcat时 ,设置JAVA_HOME和JRE_HOME
http://blog.csdn.net/xw370457549/article/details/21638827 安装tomcat后,运行startup.bat提示JAVA_HOME.JRE_HOM ...
- fully delete project in Eclipse
选择你的项目(test)右击,选择delete——弹出框中勾选删除全部,如下如所示: 正常情况下,这样就能删除干净了,有时候你项目在运行,这时候你点击删除,那就会报下面的错误提示,虽然不会影响你其它项 ...
- Ansible配置文件
官方配置文件文档 Ansible安装完成之后默认配置文件为:/etc/asnible/ansible.cfg Ansible配置文件内容: cat ansible.cfg # config file ...
- ABP 基础设施层——集成 Entity Framework
本文翻译自ABP的官方教程<EntityFramework Integration>,地址为:http://aspnetboilerplate.com/Pages/Documents/En ...