測試非同步程式碼
在 JavaScript 中,程式碼以非同步方式執行是很常見的。當您有以非同步方式執行的程式碼時,Jest 需要知道它正在測試的程式碼何時完成,才能繼續進行另一個測試。Jest 有好幾種方法可以處理這個問題。
Promises
從您的測試中傳回一個 Promise,而 Jest 將會等待該 Promise 解析。如果 Promise 被拒絕,測試將會失敗。
例如,假設 fetchData
傳回一個 Promise,而該 Promise 應該解析為字串 'peanut butter'
。我們可以用以下方式測試它
test('the data is peanut butter', () => {
return fetchData().then(data => {
expect(data).toBe('peanut butter');
});
});
Async/Await
或者,您可以在測試中使用 async
和 await
。若要撰寫非同步測試,請在傳遞給 test
的函式前面使用 async
關鍵字。例如,可以透過以下方式測試相同的 fetchData
情境
test('the data is peanut butter', async () => {
const data = await fetchData();
expect(data).toBe('peanut butter');
});
test('the fetch fails with an error', async () => {
expect.assertions(1);
try {
await fetchData();
} catch (error) {
expect(error).toMatch('error');
}
});
您可以將 async
和 await
與 .resolves
或 .rejects
結合使用。
test('the data is peanut butter', async () => {
await expect(fetchData()).resolves.toBe('peanut butter');
});
test('the fetch fails with an error', async () => {
await expect(fetchData()).rejects.toMatch('error');
});
在這些情況下,async
和 await
實際上是與承諾範例使用的相同邏輯的語法糖。
務必傳回 (或 await
) 承諾 - 如果您省略 return
/await
陳述式,您的測試將在從 fetchData
傳回的承諾解析或拒絕之前完成。
如果您預期承諾會被拒絕,請使用 .catch
方法。務必新增 expect.assertions
以驗證呼叫了特定數量的斷言。否則,已完成的承諾不會讓測試失敗。
test('the fetch fails with an error', () => {
expect.assertions(1);
return fetchData().catch(error => expect(error).toMatch('error'));
});
回呼
如果您不使用承諾,您可以使用回呼。例如,假設 fetchData
不是傳回承諾,而是預期回呼,也就是在完成時擷取一些資料並呼叫 callback(null, data)
。您想要測試這個傳回的資料是否為字串 'peanut butter'
。
預設情況下,Jest 測試在執行結束後完成。這表示這個測試不會按預期工作
// Don't do this!
test('the data is peanut butter', () => {
function callback(error, data) {
if (error) {
throw error;
}
expect(data).toBe('peanut butter');
}
fetchData(callback);
});
問題在於測試會在 fetchData
完成後立即完成,在呼叫回呼之前。
有一個 test
的替代形式可以解決這個問題。不要將測試放在具有空引數的函式中,而是使用一個稱為 done
的單一引數。Jest 會等到呼叫 done
回呼後才會完成測試。
test('the data is peanut butter', done => {
function callback(error, data) {
if (error) {
done(error);
return;
}
try {
expect(data).toBe('peanut butter');
done();
} catch (error) {
done(error);
}
}
fetchData(callback);
});
如果從未呼叫 done()
,測試將會失敗 (逾時錯誤),這正是您想要的結果。
如果 expect
陳述式失敗,它會擲回錯誤,而且不會呼叫 done()
。如果我們想要在測試記錄中看到它失敗的原因,我們必須將 expect
包裝在 try
區塊中,並將 catch
區塊中的錯誤傳遞給 done
。否則,我們會得到一個不透明的逾時錯誤,而不會顯示 expect(data)
收到的值。
如果同一個測試函式傳遞 done()
回呼並傳回承諾,Jest 會擲回錯誤。這是為了預防您的測試發生記憶體外洩。
.resolves
/ .rejects
您也可以在 expect 陳述中使用 .resolves
比對器,而 Jest 將會等待該承諾解析。如果承諾被拒絕,測試將會自動失敗。
test('the data is peanut butter', () => {
return expect(fetchData()).resolves.toBe('peanut butter');
});
務必傳回斷言,如果您省略此 return
陳述,您的測試將會在從 fetchData
傳回的承諾解析之前完成,而 then() 將有機會執行回呼。
如果您預期承諾會被拒絕,請使用 .rejects
比對器。它的運作方式類似於 .resolves
比對器。如果承諾已履行,測試將會自動失敗。
test('the fetch fails with an error', () => {
return expect(fetchData()).rejects.toMatch('error');
});
這些形式沒有任何一種特別優於其他形式,您可以在程式碼庫中或甚至在單一檔案中混合搭配它們。這取決於您覺得哪一種風格讓您的測試更為簡潔。