深入探討 Jasmine 的 Spy
Jasmine 是個 mock 與 assertion 合一的 testing framework,語法優美,寫起來程式碼的可讀性很高,重點是不複雜,學習曲線平緩,Spy 為 Jasmine 最重要的部分,本文針對最常在 Angular 使用的 Spy 做整理。
Version
Jasmine 2.6.2
Angular 4.3
建立 Spy
spyOn()
spyOn
(object: T, method: keyof T): Spy
對於 class 的 method 建立 Spy。
1 | it(`should use getTitle() method`, () => { |
測試在 HTML getTitle()
是否 binding 正確。
spyOnProperty()
spyOnProperty
(object:T, property: keyof T, accessType: string): Spy
對於 class 的 property 建立 Spy。
1 | it(`should use title in HTML`, () => { |
測試在 HTML title
是否 binding 正確。
jasmine.createSpy()
createSpy(name: string, originalFn?: Function): Spy
對於 function 或 arrow function 建立 Spy。
1 | it(`should call arrow function`, () => { |
測試 function 或 arrow function 是否被呼叫。
若要建立在 class 內的假 method,要用
spyOn()
;若要建立沒有 class 的假 function 或 arrow function,則要使用jasmine.createSpy()
。
Spy#and
returnValue()
returnValue(val: any): Spy
建立假 function 的假回傳值。1
2
3
4
5
6
7it(`should use getTitle()`, () => {
const spy = spyOn(component, 'getTitle').and.returnValue('My Todos');
fixture.detectChanges();
htmlElement = debugElement.query(By.css('h1')).nativeElement;
expect(htmlElement.textContent).toBe('My Todos');
});
使用 spyOn()
搭配 returnValue()
回傳假值,然後測試結果是否為假值。實務上有 2 個場景會使用 returnValue()
。
- 單元測試時,想要隔離相依物件,使用
returnValue()
建立假回傳值。 - 整合測試時,想要測試關係,不是測試結果,藉由假資料測試 function 是否被呼叫到。
1 | it(`should use getTitle()`, () => { |
若只是測試關係,那有更簡單的寫法,使用 spyOn()
建立 Spy 之後,測試 Spy 的 toHaveBeenCalled()
即可,不需使用 returnValue()
。
callFake()
callFake(fn: Function): Spy
建立假 function 的假實作。
1 | it(`should use getTitle() and result is 'my todos'`, () => { |
一般來說,spyOn()
搭配 returnValue()
已經非常夠用,若你想自行建立 Fake 取代原本 function 實作,則可使用 callFake()
。
callThrough()
callThrough(): Spy
依然使用原本 function,但會針對此 function 做 Spy 監測。
1 | it(`should use getTitle() and result is 'todos'`, () => { |
一般我們使用了 spyOn()
之後,就會搭配 toHaveBeenCalled()
測試有沒有被呼叫到即可,若你想除了驗證關係外,還順便結果,則可搭配 callThrough()
,此時會執行實際的 function,所以可使用 toBe()
測試結果,但仍然保有 Spy 功能,因此也可使用 toHaveBeenCalled()
測試關係。
throwError()
throwError(msg: string): Spy
建立假 function 的假 error。
1 | it(`should throw error on 'getTodos()' method`, () => { |
使用 throwError()
建立假 error。
比較特別的是,要測試 error 時,待測 method 要放在 expect()
的 arrow function 內,最後用 toThrowError()
測試 error 是否被觸發。
Spy#calls
any()
any(): boolean
若 Spy 被呼叫一次以上,則傳回 true
,否則傳回 false
。
1 | it(`should use getTitle()`, () => { |
測試 spy.calls.any()
,若曾被呼叫過為 true
,否則為 false
。
這種寫法等效於:
1 | it(`should use getTitle()`, () => { |
count()
count(): boolean
傳回 Spy 被呼叫的次數。
1 | it(`should use getTitle() for once`, () => { |
測試 spy.calls.count()
,可精確測試 Spy 被呼叫的次數。
這種寫法等效於:
1 | it(`should use getTitle() for once`, () => { |
Matcher
toHaveBeenCalled()
toHaveBeenCalled(): boolean
測試 function 是否被呼叫過。
1 | it(`should use getTitle()`, () => { |
測試 Spy 是否被呼叫過。
toHaveBeenCalledTimes()
toHaveBeenCalledTimes(expected: number): boolean
測試 function 是否被呼叫過 n 次。
1 | it(`should use getTitle() for once`, () => { |
測試 Spy 是否被呼叫過 1
次。
Conclusion
- Jasmine 的 Spy 算是 Mock 與 Spy 的合體,提供單元測試與整合測試夠用的武器,在單元測試可以藉由 Spy 隔離相依物件,在整合測試可以藉由 Spy 驗證物件之間的關係。
Reference
KeenWon, Javascript测试框架Jasmine (五):Spies
Jasmine, Jasmine Introduction