Jest | 跨越同步執行的 Jest 測試

神Q超人
Enjoy life enjoy coding
5 min readFeb 6, 2019
同步執行使測試結果出現問題

前言

上一篇「讓 Jest 為你的 Code 做測試-基礎用法教學」中提到了如何使用 Jest 做單元測試,但是 JavaScript 是屬於同步執行的程式碼,這種特性會使 Jest 在測試結果出現問題,本篇會針對這點來講解關於異步測試的方法。

異步測試

測試流程

首先建立一個用 setTimeout 的延遲模擬請求獲取資料的 function ,獲取後再將資料傳到 callBack 中執行。

這裡將函式 fetchData 放在 ./func/async.js 中:

建立測試檔案 ./__test__/async.test.js ,在檔案中匯入 fetchData ,並在 callBack 函式內設定斷言,測試 fetchData 回傳的資料是否符合期望值:

完成以上設置後,便可以執行測試了,這裡使用能夠產生覆蓋率報告的測試指令,會得到以下結果:

測試結果顯示正確,但覆蓋率卻不是 100 %

可以看到結果是 PASS ,也就是說 callBack 接收到的斷言和我們期望的值相同,但是卻發現測試的覆蓋率卻不是 100 %,代表在 fetchData 中有些地方沒有執行到,測試就已經結束了,這時可以點開 ./coverage/Icov-report 內關於 async.js 的執行報告來看:

在測試時根本沒跑進 callBack 中

發現問題

經過上方的操作,可以發現就算在測試中 JavaScript 也是一如既往同步執行,不會等到 callBack 執行,也不會到設定的斷言,整個測試就已經結束了。

這裡可以將代碼改為更直接的方式測試:

有興趣可以試試上方的測試,結果依然會是 PASS ,因為 callBack 函式根本就沒有執行到。

解決問題

雖然在 JavaScript 中,處理同步問題一直不是輕鬆的事情,但是 Jest 在執行測試的時候可以透過 done() 來應付這個狀況。

簡單來說,如果在測試裡有加入 done() ,那只要還沒執行到 done() 就不算結束測試,因此可以將它加入上方的程式裡:

加入 done() 後重新執行測試,就可以看到報告呈現了完美的一片綠光。

測試覆蓋率已達 100 %

需要注意的是,如果測試中有傳入 done 但卻未執行它,那麼該測試結果就會出現 FAIL 失敗:

執行測試結果如下:

測試時擁有參數 done 卻未執行,測試會產生錯誤

使用 Promise

如果既有的程式碼已使用 Promise 處理同步,便不必再使用 done ,直接以 .then 接收 Promise 物件傳進 resolve 的結果即可:

回傳一個 Promise 物件,將結果傳入 resolve
直接使用 .then 接收結果進行斷言

需要注意的是,在測試的 function 中必須加上 return ,否則測試不會跑進 .then 中,只會在 Promise 將結果送進 resolve 時就結束了,另外!當 Promise 中的結果跑進 reject 那測試也會產生錯誤。

測試結果得到 PASS ,覆蓋率也是 100 %:

使用 Promise 測試的結果

上述說明了成功的 resolve 在測試中用 .then 接收,而失敗的 reject 則是使用 .catch 處理接下來的動作,這部分和 Promise 的操作都相同,就不再闡述,如果對 Promise 不熟,可以參考「JacaScript | 從Promise開始承諾的部落格生活」。

Jestexpect 內還另外擁有兩個內建屬性來針對 Promise 做處理,分別為 resolvesrejects ,他們會直接捕捉 Promise 傳進resolvereject 的資料判斷是否符合期望值,這裡以 rejects 作為例子:

測試時,直接將 promiseErrorFetchData 作為參數傳遞給 expect ,並透過 expect 的內建屬性 rejects 取得 promiseErrorFetchDataPromise 傳進 reject 的資料下斷言:

這個方式筆者認為能夠更直覺性的使用 Promise 的測試,得到的結果也都和上方一樣,但主要還是配合團隊使用其中一種,才不會讓測試代碼顯得雜亂,當然!個人開發也是一樣。

本文針對異步請求在測試內產生的問題舉了幾個例子解決,但其實官網上還有提出一種使用 async/await 的方式,不過筆者還未學習到 JavaScript 中關於 asyncawait 的用法,因此怕現階段會誤導大家,就等日後使用到再回來補充文章內容。

如果文章中有任何問題,或是不理解的地方,都可以留言告訴我!謝謝大家!

參考文章

  1. https://jestjs.io/docs/en/asynchronous#async-await

--

--