深入探討 Angular 整合測試與 Jasmine 的 spyOn()
spyOn() 是整合測試與單元測試的關鍵,本文深入探討 spyOn() 用於整合測試部分。
Version
Angular CLI 1.1.2
Angular 4.2.3
定義
- 驗收測試:以 HTML 與 CSS 的角度測試最後結果,配合真資料測試,與 Angular 無關
- 整合測試:以 Angular 角度測試元件與元件之間的關係,配合假資料測試,與 Angular 有關
- 單元測試:以 class 角度測試,配合假資料測試,與 Angular 無關
最簡單的是單元測試,常常一個
expect()就可搞定其次是驗收測試,只要會寫 CSS selector 也不難
最難寫的是整合測試,要對相依物件加以假設,並傳入假資料與驗證假資料
測試目的
Q:驗收測試、整合測試與單元測試到底在測什麼?
- 驗收測試:測最終結果
- 整合測試:測之間關係
- 單元測試:測單一結果
整合測試
測試案例:
<h1></h1>內應該使用getTitle()函式
1 | it(`should use getTitle() in HTML`, () => { |
在測試 <h1></h1> 內應使用 title field 時,我們曾經指定 title 為假資料 fake,並測試 <h1></h1> 內資料是否為 fake。
同樣的技巧,我們也可以指定 component.getTitle = () => 'fake',並測試 <h1></h1> 內資料是否為 fake。
將相依物件設定為假資料,並測試目前物件是否有相依物件的假資料,為整合測試的基本招式。
SpyOn()
以上方式雖然可行,算土法煉鋼,Jasmine 另外提供更方便的方式:spyOn()。
Spy 英文為間諜,顧名思義,間諜就是假的,就是幫我們建立假物件、假資料。
若使用 spyOn(),spyOn() 會對原本的 getTitle() 進行攔胡,HTML 將無法去呼叫原本 TitleComponent.getTitle(),而改呼叫 Spy.getTitle()。
spyOn() 另外一個特點,就是對不存在的相依物件進行 spyOn() 時,會測試紅燈,可藉此驅動出相依物件的單元測試,這也是 ATDD 能 out side in 的關鍵。
callFake()
1 | it(`should use getTitle() in HTML`, () => { |
callFake() 會傳入 arrow function 回傳假資料,用來取代想 spyOn() 的 function。
最後 expect() 一樣測試假資料。
returnValue()
1 | it(`should use getTitle() in HTML`, () => { |
直接使用 returnValue() 回傳假資料,寫法比 callFake() 簡潔。
最後 expect() 一樣測試假資料。
calls.any()
1 | it(`should use getTitle() in HTML`, () => { |
整合測試重點是測試之間的關係,而測試假資料只是一種手段,若能直接測試 getTitle() 是有被呼叫到,會更符合整合測試的精神。
使用 spyOn() 回傳一個物件,在 expect() 內測試 spy.calls.any(),若該 spyOn() 的 function 有被呼叫到則為 true。
toHaveBeenCalled()
1 | it(`should use getTitle() in HTML`, () => { |
以整合測試的角度,我們想測試的是之間的關係,也就是重點是有沒有被呼叫到,之前用假資料測試假資料只是一種手段,若 Jasmine 能直接 expect() 不就更理想?
Jasmine 提供了 expect().toHaveBeenCalled(),若 spyOn() 的 function 被呼叫執行過,就會傳回 true。
toHaveBeenCalled()是語意最佳的整合測試寫法,強烈推薦。
SpyOn() 使用時機
- 整合測試:用來對尚未實作的相依物件建立假物件,讓目前的整合測試
紅燈,驅動出相依物件的單元測試紅燈。 - 單元測試:用來對已經實作的相依物件建立假物件,讓目前的單元測試
綠燈。
1 是 ATDD 的理論基礎。
2 是 TDD 的理論基礎。
Conclusion
- Jasmine 的
spyOn()無法對 field 加以 spy,因此必須使用整合測試的基本技巧:使用假值並測試假值,但 method 可直接spyOn(),建議直接搭配toHaveBeenCalled()。 - 大部分人都是在單元測試使用
spyOn(),將相依物件加以隔離,但事實上在整合測試使用spyOn()配合toHaveBeenCalled(),還可以驅動出相依物件的單元測試,這正是 ATDD 由整合測試紅燈,驅動出單元測試紅燈的關鍵技術。