Jest 測(cè)試異步代碼

2021-09-23 20:13 更新

在JavaScript中執(zhí)行異步代碼是很常見(jiàn)的。 當(dāng)你有以異步方式運(yùn)行的代碼時(shí),Jest 需要知道當(dāng)前它測(cè)試的代碼是否已完成,然后它可以轉(zhuǎn)移到另一個(gè)測(cè)試。 Jest有若干方法處理這種情況。

回調(diào)

最常見(jiàn)的異步模式是回調(diào)函數(shù)。

例如,假設(shè)你有一個(gè) ?fetchData(callback)? 函數(shù),獲取一些數(shù)據(jù)并在完成時(shí)調(diào)用 ?callback(data)?。 你期望返回的數(shù)據(jù)是一個(gè)字符串 ?'peanut butter'?

默認(rèn)情況下,Jest 測(cè)試一旦執(zhí)行到末尾就會(huì)完成。 那意味著該測(cè)試將不會(huì)按預(yù)期工作:

  1. // 不要這樣做!
  2. test('the data is peanut butter', () => {
  3. function callback(data) {
  4. expect(data).toBe('peanut butter');
  5. }
  6. fetchData(callback);
  7. });

問(wèn)題在于一旦?fetchData?執(zhí)行結(jié)束,此測(cè)試就在沒(méi)有調(diào)用回調(diào)函數(shù)前結(jié)束。

還有另一種形式的 ?test?,解決此問(wèn)題。 使用單個(gè)參數(shù)調(diào)用 ?done?,而不是將測(cè)試放在一個(gè)空參數(shù)的函數(shù)。 Jest會(huì)等?done?回調(diào)函數(shù)執(zhí)行結(jié)束后,結(jié)束測(cè)試。

  1. test('the data is peanut butter', done => {
  2. function callback(data) {
  3. try {
  4. expect(data).toBe('peanut butter');
  5. done();
  6. } catch (error) {
  7. done(error);
  8. }
  9. }
  10. fetchData(callback);
  11. });

若 ?done()? 函數(shù)從未被調(diào)用,測(cè)試用例會(huì)正如你預(yù)期的那樣執(zhí)行失敗(顯示超時(shí)錯(cuò)誤)。

若 ?expect ?執(zhí)行失敗,它會(huì)拋出一個(gè)錯(cuò)誤,后面的 ?done()? 不再執(zhí)行。 若我們想知道測(cè)試用例為何失敗,我們必須將 ?expect ?放入 ?try ?中,將 ?error ?傳遞給 ?catch ?中的 ?done?函數(shù)。 否則,最后控制臺(tái)將顯示一個(gè)超時(shí)錯(cuò)誤失敗,不能顯示我們?cè)?nbsp;?expect(data)? 中接收的值。

Promise

如果你的代碼使用了Promise?,那么有一種更直接的方法來(lái)處理異步測(cè)試。從測(cè)試中返回一個(gè) Promise,Jest將等待該 Promise 得到解決。如果 Promise 被拒絕,則測(cè)試將自動(dòng)失敗。

舉個(gè)例子,如果 ?fetchData ?不使用回調(diào)函數(shù),而是返回一個(gè) Promise,其解析值為字符串? 'peanut butter' ?我們可以這樣測(cè)試:

  1. test('the data is peanut butter', () => {
  2. return fetchData().then(data => {
  3. expect(data).toBe('peanut butter');
  4. });
  5. });

一定不要忘記把 promise 作為返回值?如果你忘了 ?return ?語(yǔ)句的話,在 ?fetchData ?返回的這個(gè) promise 被 resolve、then() 有機(jī)會(huì)執(zhí)行之前,測(cè)試就已經(jīng)被視為已經(jīng)完成了。

如果你期望的 Promise 被拒絕,請(qǐng)使用?.catch?方法。 請(qǐng)確保添加 ?expect.assertions? 來(lái)驗(yàn)證一定數(shù)量的斷言被調(diào)用。 否則,履行的 Promise 不會(huì)通過(guò)測(cè)試。

  1. test('the fetch fails with an error', () => {
  2. expect.assertions(1);
  3. return fetchData().catch(e => expect(e).toMatch('error'));
  4. });

?.resolves? / ?.rejects?

你也可以在 ?expect ?語(yǔ)句中使用? .resolves? 匹配器,Jest 將等待此 Promise 解決。 如果 Promise 被拒絕,則測(cè)試將自動(dòng)失敗。

  1. test('the data is peanut butter', () => {
  2. return expect(fetchData()).resolves.toBe('peanut butter');
  3. });

一定不要忘記把整個(gè)斷言作為返回值返回?如果你忘了?return?語(yǔ)句的話,在 ?fetchData ?返回的這個(gè) promise 變更為 resolved 狀態(tài)、then() 有機(jī)會(huì)執(zhí)行之前,測(cè)試就已經(jīng)被視為已經(jīng)完成了。

如果你期望 Promise 被拒絕,請(qǐng)使用.rejects匹配器。它參照工程 ?.resolves? 匹配器。 如果 Promise 被拒絕,則測(cè)試將自動(dòng)失敗。

  1. test('the fetch fails with an error', () => {
  2. return expect(fetchData()).rejects.toMatch('error');
  3. });

異步/等待

或者,你也可以在測(cè)試中使用 ?async? 和 ?await?。 要編寫(xiě)異步測(cè)試,請(qǐng)?jiān)趥鬟f給?test?的函數(shù)前面使用?async?關(guān)鍵字。.例如,可以用來(lái)測(cè)試相同的 ?fetchData ?方案︰

  1. test('the data is peanut butter', async () => {
  2. const data = await fetchData();
  3. expect(data).toBe('peanut butter');
  4. });
  5. test('the fetch fails with an error', async () => {
  6. expect.assertions(1);
  7. try {
  8. await fetchData();
  9. } catch (e) {
  10. expect(e).toMatch('error');
  11. }
  12. });

你可以將?async?和?await?與?.resolves?或結(jié)合?.rejects?使用??。

  1. test('the data is peanut butter', async () => {
  2. await expect(fetchData()).resolves.toBe('peanut butter');
  3. });
  4. test('the fetch fails with an error', async () => {
  5. await expect(fetchData()).rejects.toThrow('error');
  6. });

在這些情況下,對(duì)于與 promise 示例使用的相同邏輯?async?,?await?它們是有效的語(yǔ)法糖。

上述的諸多形式中沒(méi)有哪個(gè)形式特別優(yōu)于其他形式,你可以在整個(gè)代碼庫(kù)中,甚至也可以在單個(gè)文件中混合使用它們。 這只取決于哪種形式更能使你的測(cè)試變得簡(jiǎn)單。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)