jasmine —— Spies(转)
Jasmine有称为间谍(spies)的测试双重功能。一个spy可以监测任何函数的调用和参数的调用痕迹。Spy只能存在于定义它的describe()
和it()
代码块内,而在每一个spec(即it)结束后将被移除。(这个语法在Jasmine2.0才改变的)
有几个特别的Matchers与spy相互作用: toHaveBeenCalled()
:在spy被调用是返回true; toHaveBeenCalledTimes()
:在spy调用指定次数的时候会通过测试; toHaveBeenCalledWith()
:如果匹配任一调用的参数列表,则返回true。
例子:
describe("A spy", function() { var foo, bar = null; beforeEach(function() { foo = { setBar: function(value) { bar = value; } }; /*使用spyOn()来声明spy*/ spyOn(foo, 'setBar'); foo.setBar(123); foo.setBar(456, 'another param'); }); it("tracks that the spy was called", function() { /*测试spy是否调用*/ expect(foo.setBar).toHaveBeenCalled(); }); it("tracks that the spy was called x times", function() { /*测试spy是否调用两次*/ expect(foo.setBar).toHaveBeenCalledTimes(2); }); it("tracks all the arguments of its calls", function() { /*测试spy调用时的参数列表是否匹配*/ expect(foo.setBar).toHaveBeenCalledWith(123); expect(foo.setBar).toHaveBeenCalledWith(456, 'another param'); }); it("stops all execution on a function", function() { /*spy的调用并不会影响真实的值,所以bar仍然是null*/ expect(bar).toBeNull(); }); });
Spy通过链式调用and.callThrough
,除了追踪所有的调用之外,它将委托实际的实现值。例如:
1 describe("A spy, when configured to call through", function() { 2 var foo, bar, fetchedBar; 3 4 beforeEach(function() { 5 foo = { 6 setBar: function(value) { 7 bar = value; 8 }, 9 getBar: function() { 10 return bar; 11 } 12 }; 13 14 spyOn(foo, 'getBar').and.callThrough(); //定义spy并且链式调用 15 16 foo.setBar(123); 17 fetchedBar = foo.getBar(); //调用spy 18 }); 19 20 it("tracks that the spy was called", function() { 21 expect(foo.getBar).toHaveBeenCalled(); 22 }); 23 24 it("should not affect other functions", function() { 25 expect(bar).toEqual(123); 26 }); 27 28 it("when called returns the requested value", function() { 29 expect(fetchedBar).toEqual(123); //fetchedBar为函数实际返回的值。 30 }); 31 });
将上述代码与以下代码对比:
1 describe("A spy, when configured to call through", function() { 2 var foo, bar, fetchedBar; 3 4 beforeEach(function() { 5 foo = { 6 setBar: function(value) { 7 bar = value; 8 }, 9 getBar: function() { 10 return bar; 11 } 12 }; 13 14 spyOn(foo, 'getBar'); //不再链式调用and.callThrough() 15 16 foo.setBar(123); 17 fetchedBar = foo.getBar(); 18 }); 19 20 it("tracks that the spy was called", function() { 21 expect(foo.getBar).toHaveBeenCalled(); 22 }); 23 24 it("should not affect other functions", function() { 25 expect(bar).toEqual(123); 26 }); 27 28 it("when called returns the requested value", function() { 29 expect(fetchedBar).toBeUndefined(); //此时的fetchedBar不再是函数返回的实际值,而是undefined 30 }); 31 });
Spy通过链式调用and.returnValue
,所有调用spy的都将返回一个指定值。例如:
1 describe("A spy, when configured to fake a return value", function() { 2 var foo, bar, fetchedBar; 3 4 beforeEach(function() { 5 foo = { 6 setBar: function(value) { 7 bar = value; 8 }, 9 getBar: function() { 10 return bar; 11 } 12 }; 13 14 spyOn(foo, "getBar").and.returnValue(745); //指定返回745 15 16 foo.setBar(123); 17 fetchedBar = foo.getBar(); 18 }); 19 20 it("tracks that the spy was called", function() { 21 expect(foo.getBar).toHaveBeenCalled(); 22 }); 23 24 it("should not affect other functions", function() { 25 expect(bar).toEqual(123); 26 }); 27 28 it("when called returns the requested value", function() { 29 expect(fetchedBar).toEqual(745); //返回特定值为745 30 }); 31 });
Spy通过链式调用and.returnValues
,所有调用该spy的函数都将按顺序返回一些特定的值,直到返回值队列的最后,这之后的所有调用将返回undefined
。例如:
1 describe("A spy, when configured to fake a series of return values", function() { 2 var foo, bar; 3 4 beforeEach(function() { 5 foo = { 6 setBar: function(value) { 7 bar = value; 8 }, 9 getBar: function() { 10 return bar; 11 } 12 }; 13 14 spyOn(foo, "getBar").and.returnValues("fetched first", "fetched second"); 15 16 foo.setBar(123); 17 }); 18 19 it("tracks that the spy was called", function() { 20 foo.getBar(123); 21 expect(foo.getBar).toHaveBeenCalled(); 22 }); 23 24 it("should not affect other functions", function() { 25 expect(bar).toEqual(123); 26 }); 27 28 it("when called multiple times returns the requested values in order", function() { 29 expect(foo.getBar()).toEqual("fetched first"); //第一次调用,返回队列的第一个值 30 expect(foo.getBar()).toEqual("fetched second"); //第二次调用,返回队列的第二个值 31 expect(foo.getBar()).toBeUndefined(); //之后的调用都将返回undefined 32 }); 33 });
Spy通过调用and.callFake
,所有调用该spy的都将调用其提供的函数,例如:
1 describe("A spy, when configured with an alternate implementation", function() { 2 var foo, bar, fetchedBar; 3 4 beforeEach(function() { 5 foo = { 6 setBar: function(value) { 7 bar = value; 8 }, 9 getBar: function() { 10 return bar; 11 } 12 }; 13 14 spyOn(foo, "getBar").and.callFake(function() { 15 return 1001; 16 }); 17 18 foo.setBar(123); 19 fetchedBar = foo.getBar(); 20 }); 21 22 it("tracks that the spy was called", function() { 23 expect(foo.getBar).toHaveBeenCalled(); 24 }); 25 26 it("should not affect other functions", function() { 27 expect(bar).toEqual(123); 28 }); 29 30 it("when called returns the requested value", function() { 31 expect(fetchedBar).toEqual(1001); 32 }); 33 });
Spy链式调用and.throwError
,调用该spy的将以一个错误的形式抛出特殊返回值,例如:
1 describe("A spy, when configured to throw an error", function() { 2 var foo, bar; 3 4 beforeEach(function() { 5 foo = { 6 setBar: function(value) { 7 bar = value; 8 } 9 }; 10 11 spyOn(foo, "setBar").and.throwError("quux"); 12 }); 13 14 it("throws the value", function() { 15 expect(function() { 16 foo.setBar(123) 17 }).toThrowError("quux"); 18 }); 19 });
Spy链式调用以上某一个策略后,可以调用and.stub
随时返回之前保存的原始数据,而不进行修改。例如:
1 describe("A spy", function() { 2 var foo, bar = null; 3 4 beforeEach(function() { 5 foo = { 6 setBar: function(value) { 7 bar = value; 8 } 9 }; 10 11 spyOn(foo, 'setBar').and.callThrough(); 12 }); 13 14 it("can call through and then stub in the same spec", function() { 15 foo.setBar(123); //调用策略and.callThrough()所定义的spy 16 expect(bar).toEqual(123); //bar数值被修改为123 17 18 foo.setBar.and.stub(); //调用and.stub() 19 bar = null; 20 21 foo.setBar(123);//调用spy 22 expect(bar).toBe(null); //bar不再返回and.callThrough()的实现值 23 }); 24 });
其他跟踪属性
任何调用spy的都将被追踪,并且暴露在calls
的属性中。
calls属性有:
1、.calls.any() : 一次都没调用时返回false,一旦调用至少一次就返回true;
2. .calls.count()
:返回spy调用的次数
3. .calls.argsFor(index)
:返回第index+1次调用时传递的参数,index从0开始;
4. .calls.allArgs()
:以数组的形式返回调用时传递的所有参数;
5. .calls.all()
:以对象形式返回上下文(this),以及所有传递的参数;
6. .calls.mostRecent()
:以对象形式返回最近一次调用的上下文(this),以及传递的参数;
7. .calls.first()
:以对象形式返回第一次调用的上下文(this),以及传递的参数;(当检查.calls.all()
,.calls.mostRecent()
,.calls.first()
返回的对象时,.object
属性指向的是调用该spy的this对象)
8. .calls.reset()
:清空spy的所有追踪。
上述属性值,例子如下:
1 describe("A spy", function() { 2 var foo, bar = null; 3 4 beforeEach(function() { 5 foo = { 6 setBar: function(value) { 7 bar = value; 8 } 9 }; 10 11 spyOn(foo, 'setBar'); 12 }); 13 it("tracks if it was called at all", function() { 14 expect(foo.setBar.calls.any()).toEqual(false); 15 16 foo.setBar(); 17 18 expect(foo.setBar.calls.any()).toEqual(true); 19 }); 20 it("tracks the number of times it was called", function() { 21 expect(foo.setBar.calls.count()).toEqual(0); 22 23 foo.setBar(); 24 foo.setBar(); 25 26 expect(foo.setBar.calls.count()).toEqual(2); 27 }); 28 it("tracks the arguments of each call", function() { 29 foo.setBar(123); 30 foo.setBar(456, "baz"); 31 32 expect(foo.setBar.calls.argsFor(0)).toEqual([123]); 33 expect(foo.setBar.calls.argsFor(1)).toEqual([456, "baz"]); 34 }); 35 it("tracks the arguments of all calls", function() { 36 foo.setBar(123); 37 foo.setBar(456, "baz"); 38 39 expect(foo.setBar.calls.allArgs()).toEqual([[123],[456, "baz"]]); 40 }); 41 it("can provide the context and arguments to all calls", function() { 42 foo.setBar(123); 43 44 expect(foo.setBar.calls.all()).toEqual([{object: foo, args: [123], returnValue: undefined}]); 45 }); 46 it("has a shortcut to the most recent call", function() { 47 foo.setBar(123); 48 foo.setBar(456, "baz"); 49 50 expect(foo.setBar.calls.mostRecent()).toEqual({object: foo, args: [456, "baz"], returnValue: undefined}); 51 }); 52 it("has a shortcut to the first call", function() { 53 foo.setBar(123); 54 foo.setBar(456, "baz"); 55 56 expect(foo.setBar.calls.first()).toEqual({object: foo, args: [123], returnValue: undefined}); 57 }); 58 it("tracks the context", function() { 59 var spy = jasmine.createSpy('spy'); 60 var baz = { 61 fn: spy 62 }; 63 var quux = { 64 fn: spy 65 }; 66 baz.fn(123); 67 quux.fn(456); 68 69 //当检查.calls.all(),.calls.mostRecent(),.calls.first()返回的对象时,.object属性指向的是调用该spy的this对象 70 expect(spy.calls.first().object).toBe(baz); 71 expect(spy.calls.mostRecent().object).toBe(quux); 72 }); 73 it("can be reset", function() { 74 foo.setBar(123); 75 foo.setBar(456, "baz"); 76 77 expect(foo.setBar.calls.any()).toBe(true); 78 79 foo.setBar.calls.reset(); 80 81 expect(foo.setBar.calls.any()).toBe(false); 82 }); 83 });
Spies:createSpy
如果没有一个函数可以spyOn,jasmine.createSpy
可以创建一个“空白”的spy。该spy会像其他间谍一样追踪调用,函数等等,但是在其之后并不会有实际实现的返回值。Spies是JavaScript对象,可以这样使用:
1 describe("A spy, when created manually", function() { 2 var whatAmI; 3 4 beforeEach(function() { 5 whatAmI = jasmine.createSpy('whatAmI'); 6 7 whatAmI("I", "am", "a", "spy"); 8 }); 9 10 it("is named, which helps in error reporting", function() { 11 expect(whatAmI.and.identity()).toEqual('whatAmI'); 12 }); 13 14 it("tracks that the spy was called", function() { 15 expect(whatAmI).toHaveBeenCalled(); 16 }); 17 18 it("tracks its number of calls", function() { 19 expect(whatAmI.calls.count()).toEqual(1); 20 }); 21 22 it("tracks all the arguments of its calls", function() { 23 expect(whatAmI).toHaveBeenCalledWith("I", "am", "a", "spy"); 24 }); 25 26 it("allows access to the most recent call", function() { 27 expect(whatAmI.calls.mostRecent().args[0]).toEqual("I"); 28 }); 29 });
Spies:createSpyObj
为了创建一个多重spies的模拟,使用jasmine.createSpyObj()
并传递一个字符串的数组,将会返回一个对象,对象包括每个字符串绑定的spy属性。例如:
1 describe("Multiple spies, when created manually", function() { 2 var tape; 3 4 beforeEach(function() { 5 tape = jasmine.createSpyObj('tape', ['play', 'pause', 'stop', 'rewind']); 6 7 tape.play(); 8 tape.pause(); 9 tape.rewind(0); 10 }); 11 12 it("creates spies for each requested function", function() { 13 expect(tape.play).toBeDefined(); 14 expect(tape.pause).toBeDefined(); 15 expect(tape.stop).toBeDefined(); 16 expect(tape.rewind).toBeDefined(); 17 }); 18 19 it("tracks that the spies were called", function() { 20 expect(tape.play).toHaveBeenCalled(); 21 expect(tape.pause).toHaveBeenCalled(); 22 expect(tape.rewind).toHaveBeenCalled(); 23 expect(tape.stop).not.toHaveBeenCalled(); 24 }); 25 26 it("tracks all the arguments of its calls", function() { 27 expect(tape.rewind).toHaveBeenCalledWith(0); 28 }); 29 });