寫在前面:
在JavaScript代碼中,異步運行是很常見的。當你有異步運行的代碼時,Jest需要知道它測試的代碼何時完成,然后才能繼續進行另一個測試。Jest提供了幾種方法來處理這個問題。
測試異步代碼的三種實現方式:
方法一:回調函數
這是非常常見的通用處理方式,比如你有一個fetchData(callback)的
function用來獲取數據,並且在獲取完成的時候調用callback 函數,你想測試返回的數據是“peanut butter” ,默認情況下當fetchData執行完成的時候Jest的測試就完成了,這並不是你所期望的那樣的去運行。
// Don't do this! test('the data is peanut butter', () => { function callback(data) { expect(data).toBe('peanut butter'); } fetchData(callback); });
上面代碼的問題就在於一旦fetchData完成,測試也就執行完成,然后再調用回調。
Jest提供了一種用於測試的實現方式,下面代碼 done() 被執行則意味着callback函數被調用。
test('the data is peanut butter', done => { function callback(data) { expect(data).toBe('peanut butter'); done(); } fetchData(callback); });
如果 done 永遠都不被調用,那么的測試將會失敗,這也正是我們期望的(我們希望callback被調用,並且返回的data是我們期望的值)
方法二:承諾驗證
如果你的代碼中使用了承諾的方式,處理異步測試將會變得更加簡單。Jest從你的測試中返回一個承諾,然后等待承諾被實現,如果沒有實現,那么就判斷測試是失敗的。
還是上面的例子,如果用承諾驗證,那么實現將是下面的樣子:
test('the data is peanut butter', () => { expect.assertions(1); return fetchData().then(data => { expect(data).toBe('peanut butter'); }); });
assertions(1)代表的是在當前的測試中至少有一個斷言是被調用的,否則判定為失敗。
如果刪掉return語句,那么你的測試將在fetchData完成之前結束。
如果斷言的承諾並沒有被實現,那么你可以添加 .catch 方法。一定要添加expect,斷言驗證一定數量的斷言被調用。否則一個實現的承諾就不會失敗。
test('the fetch fails with an error', () => { expect.assertions(1); return fetchData().catch(e => expect(e).toMatch('error')); });
在Jest 20.0.0+ 的版本中你可以使用 .resolves 匹配器在你的expect語句中,Jest將會等待一直到承諾被實現,如果承諾沒有被實現,測試將自動失敗。
test('the data is peanut butter', () => { expect.assertions(1); return expect(fetchData()).resolves.toBe('peanut butter'); });
如果你期望你的承諾是不被實現的,你可以使用 .rejects ,它的原理和 .resolves類似
test('the fetch fails with an error', () => { expect.assertions(1); return expect(fetchData()).rejects.toMatch('error'); });
第三種:使用 Async/Await
我相信大家最Async/Await 是比較熟悉的,你可以在測試中使用異步和等待。要編寫一個async測試,只需在傳遞到測試的函數前面使用async關鍵字。例如上面同樣的fetchData場景可以使用下面的實現:
test('the data is peanut butter', async () => { expect.assertions(1); const data = await fetchData(); expect(data).toBe('peanut butter'); }); test('the fetch fails with an error', async () => { expect.assertions(1); try { await fetchData(); } catch (e) { expect(e).toMatch('error'); } });
當然你也可以將Async Await和 .resolves .rejects 結合起來(Jest 20.0.0+ 的版本)
test('the data is peanut butter', async () => { expect.assertions(1); await expect(fetchData()).resolves.toBe('peanut butter'); }); test('the fetch fails with an error', async () => { expect.assertions(1); await expect(fetchData()).rejects.toMatch('error'); });
寫在最后:
在這些情況下,異步和等待實際上只是與承諾示例使用的相同邏輯的語法糖。
這幾種方法中沒有一個特別優於另外一個,你可以將它們組合在一個代碼庫中,甚至可以在單個文件中進行匹配。它只是取決於哪種樣式使你的測試更簡單。
系列教程:
1. 前端測試框架Jest系列教程 -- Matchers(匹配器)
2.前端測試框架Jest系列教程 -- Asynchronous(測試異步代碼)
3.前端測試框架Jest系列教程 -- Mock Functions(模擬器)
4.前端測試框架Jest系列教程 -- Global Functions(全局函數)