在JavaScript中執(zhí)行異步代碼是很常見(jiàn)的。 當(dāng)你有以異步方式運(yùn)行的代碼時(shí),Jest 需要知道當(dāng)前它測(cè)試的代碼是否已完成,然后它可以轉(zhuǎn)移到另一個(gè)測(cè)試。 Jest有若干方法處理這種情況。
最常見(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ù)期工作:
// 不要這樣做!
test('the data is peanut butter', () => {
function callback(data) {
expect(data).toBe('peanut butter');
}
fetchData(callback);
});
問(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è)試。
test('the data is peanut butter', done => {
function callback(data) {
try {
expect(data).toBe('peanut butter');
done();
} catch (error) {
done(error);
}
}
fetchData(callback);
});
若 ?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
?,那么有一種更直接的方法來(lái)處理異步測(cè)試。從測(cè)試中返回一個(gè) Promise,Jest將等待該 Promise 得到解決。如果 Promise 被拒絕,則測(cè)試將自動(dòng)失敗。
舉個(gè)例子,如果 ?fetchData
?不使用回調(diào)函數(shù),而是返回一個(gè) Promise,其解析值為字符串? 'peanut butter'
?我們可以這樣測(cè)試:
test('the data is peanut butter', () => {
return fetchData().then(data => {
expect(data).toBe('peanut butter');
});
});
一定不要忘記把 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è)試。
test('the fetch fails with an error', () => {
expect.assertions(1);
return fetchData().catch(e => expect(e).toMatch('error'));
});
.resolves
? / ?.rejects
?你也可以在 ?expect ?語(yǔ)句中使用? .resolves
? 匹配器,Jest 將等待此 Promise 解決。 如果 Promise 被拒絕,則測(cè)試將自動(dòng)失敗。
test('the data is peanut butter', () => {
return expect(fetchData()).resolves.toBe('peanut butter');
});
一定不要忘記把整個(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)失敗。
test('the fetch fails with an error', () => {
return expect(fetchData()).rejects.toMatch('error');
});
或者,你也可以在測(cè)試中使用 ?async
? 和 ?await
?。 要編寫(xiě)異步測(cè)試,請(qǐng)?jiān)趥鬟f給?test
?的函數(shù)前面使用?async
?關(guān)鍵字。.例如,可以用來(lái)測(cè)試相同的 ?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 (e) {
expect(e).toMatch('error');
}
});
你可以將?async
?和?await
?與?.resolves
?或結(jié)合?.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.toThrow('error');
});
在這些情況下,對(duì)于與 promise 示例使用的相同邏輯?async
?,?await
?它們是有效的語(yǔ)法糖。
上述的諸多形式中沒(méi)有哪個(gè)形式特別優(yōu)于其他形式,你可以在整個(gè)代碼庫(kù)中,甚至也可以在單個(gè)文件中混合使用它們。 這只取決于哪種形式更能使你的測(cè)試變得簡(jiǎn)單。
更多建議: