Jasmine文件(二)
前言
這是翻譯的Jasmine文件第二篇,第一篇文章的地址為:
Jasmine文件(一)
Spies(間諜)
Jasmine有稱為間諜(spies)的測試雙重功能。一個spy可以監測任何函式的呼叫和引數的呼叫痕跡。Spy只能存在於定義它的describe()
和it()
程式碼塊內,而在每一個spec結束後將被移除。(這個語法在Jasmine2.0才改變的)
有幾個特別的Matchers於spy相互作用:
toHaveBeenCalled()
:在spy被呼叫是返回true;
toHaveBeenCalledTimes()
:在spy呼叫指定次數的時候會通過測試;
toHaveBeenCalledWith()
例子:
describe("A spy", function() {
var foo, bar = null;
beforeEach(function() {
foo = {
setBar: function(value) {
bar = value;
}
};
spyOn(foo, 'setBar');//使用spyOn()來宣告spy
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();
});
});
and.callThrough
Spy通過鏈式呼叫and.callThrough
,除了追蹤所有的呼叫之外,它將委託實際的實現值。例如:
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();//定義spy並且鏈式呼叫and.callThrough()
foo.setBar(123);
fetchedBar = foo.getBar();//呼叫spy
});
it("tracks that the spy was called", function() {
expect(foo.getBar).toHaveBeenCalled();
});
it("should not affect other functions", function() {
expect(bar).toEqual(123);
});
it("when called returns the requested value", function() {
expect(fetchedBar).toEqual(123);//fetchedBar為函式實際返回的值。
});
});
將上述程式碼對比於一下程式碼:
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()
foo.setBar(123);
fetchedBar = foo.getBar();
});
it("tracks that the spy was called", function() {
expect(foo.getBar).toHaveBeenCalled();
});
it("should not affect other functions", function() {
expect(bar).toEqual(123);
});
it("when called returns the requested value", function() {
expect(fetchedBar).toBeUndefined();//此時的fetchedBar不再是函式返回的實際值,而是undefined
});
});
and.returnValue
Spy通過鏈式呼叫and.returnValue
,所有呼叫spy的都將返回一個指定值。例如:
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);//指定返回745
foo.setBar(123);
fetchedBar = foo.getBar();
});
it("tracks that the spy was called", function() {
expect(foo.getBar).toHaveBeenCalled();
});
it("should not affect other functions", function() {
expect(bar).toEqual(123);
});
it("when called returns the requested value", function() {
expect(fetchedBar).toEqual(745);//返回特定值為745
});
});
and.returnValues
Spy通過鏈式呼叫and.returnValues
,所有呼叫該spy的函式都將按順序返回一些特定的值,直到返回值佇列的最後,這之後的所有呼叫將返回undefined
。例如:
describe("A spy, when configured to fake a series of return values", function() {
var foo, bar;
beforeEach(function() {
foo = {
setBar: function(value) {
bar = value;
},
getBar: function() {
return bar;
}
};
spyOn(foo, "getBar").and.returnValues("fetched first", "fetched second");
foo.setBar(123);
});
it("tracks that the spy was called", function() {
foo.getBar(123);
expect(foo.getBar).toHaveBeenCalled();
});
it("should not affect other functions", function() {
expect(bar).toEqual(123);
});
it("when called multiple times returns the requested values in order", function() {
expect(foo.getBar()).toEqual("fetched first");//第一次呼叫,返回佇列的第一個值
expect(foo.getBar()).toEqual("fetched second");//第二次呼叫,返回佇列的第二個值
expect(foo.getBar()).toBeUndefined();//之後的呼叫都將返回undefined
});
});
and.callFake
Spy通過呼叫and.callFake
,所有呼叫該spy的都將呼叫其提供的函式,例如:
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 affect other functions", function() {
expect(bar).toEqual(123);
});
it("when called returns the requested value", function() {
expect(fetchedBar).toEqual(1001);
});
});
and.throwError
Spy鏈式呼叫and.throwError
,呼叫該spy的將以一個錯誤的形式丟擲特殊返回值,例如:
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
Spy鏈式呼叫以上某一個策略後,可以呼叫and.stub
隨時返回之前儲存的原始資料,而不進行修改。例如:
describe("A spy", function() {
var foo, bar = null;
beforeEach(function() {
foo = {
setBar: function(value) {
bar = value;
}
};
spyOn(foo, 'setBar').and.callThrough();
});
it("can call through and then stub in the same spec", function() {
foo.setBar(123);//呼叫策略and.callThrough()所定義的spy
expect(bar).toEqual(123);//bar數值被修改為123
foo.setBar.and.stub();//呼叫and.stub()
bar = null;
foo.setBar(123);//呼叫spy
expect(bar).toBe(null);//bar不再返回and.callThrough()的實現值
});
});
其他跟蹤屬性
任何呼叫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的所有追蹤。
上述屬性值,例子如下:
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);
//當檢查.calls.all(),.calls.mostRecent(),.calls.first()返回的物件時,.object屬性指向的是呼叫該spy的this物件
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);
});
});
Spies:createSpy
如果沒有一個函式可以spyOn,jasmine.createSpy
可以建立一個“空白”的spy。該spy會像其他間諜一樣追蹤呼叫,函式等等,但是在其之後並不會有實際實現的返回值。Spies是JavaScript物件,可以這樣使用:
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");
});
});
Spies:createSpyObj
為了建立一個多重spies的模擬,使用jasmine.createSpyObj()
並傳遞一個字串的陣列,將會返回一個物件,物件包括每個字串繫結的spy屬性。例如:
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);
});
});