在JavaScript中執(zhí)行異步代碼是很常見的。 當你有以異步方式運行的代碼時,Jest 需要知道當前它測試的代碼是否已完成,然后它可以轉(zhuǎn)移到另一個測試。 Jest有若干方法處理這種情況。
最常見的異步模式是回調(diào)函數(shù)。
例如,假設(shè)你有一個 ?fetchData(callback)
? 函數(shù),獲取一些數(shù)據(jù)并在完成時調(diào)用 ?callback(data)
?。 你期望返回的數(shù)據(jù)是一個字符串 ?'peanut butter'
?
默認情況下,Jest 測試一旦執(zhí)行到末尾就會完成。 那意味著該測試將不會按預(yù)期工作:
// 不要這樣做!
test('the data is peanut butter', () => {
function callback(data) {
expect(data).toBe('peanut butter');
}
fetchData(callback);
});
問題在于一旦?fetchData
?執(zhí)行結(jié)束,此測試就在沒有調(diào)用回調(diào)函數(shù)前結(jié)束。
還有另一種形式的 ?test
?,解決此問題。 使用單個參數(shù)調(diào)用 ?done
?,而不是將測試放在一個空參數(shù)的函數(shù)。 Jest會等?done
?回調(diào)函數(shù)執(zhí)行結(jié)束后,結(jié)束測試。
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)用,測試用例會正如你預(yù)期的那樣執(zhí)行失?。@示超時錯誤)。
若 ?expect
?執(zhí)行失敗,它會拋出一個錯誤,后面的 ?done()
? 不再執(zhí)行。 若我們想知道測試用例為何失敗,我們必須將 ?expect
?放入 ?try
?中,將 ?error
?傳遞給 ?catch
?中的 ?done
?函數(shù)。 否則,最后控制臺將顯示一個超時錯誤失敗,不能顯示我們在 ?expect(data)
? 中接收的值。
如果你的代碼使用了Promise
?,那么有一種更直接的方法來處理異步測試。從測試中返回一個 Promise,Jest將等待該 Promise 得到解決。如果 Promise 被拒絕,則測試將自動失敗。
舉個例子,如果 ?fetchData
?不使用回調(diào)函數(shù),而是返回一個 Promise,其解析值為字符串? 'peanut butter'
?我們可以這樣測試:
test('the data is peanut butter', () => {
return fetchData().then(data => {
expect(data).toBe('peanut butter');
});
});
一定不要忘記把 promise 作為返回值?如果你忘了 ?return
?語句的話,在 ?fetchData
?返回的這個 promise 被 resolve、then() 有機會執(zhí)行之前,測試就已經(jīng)被視為已經(jīng)完成了。
如果你期望的 Promise 被拒絕,請使用?.catch
?方法。 請確保添加 ?expect.assertions
? 來驗證一定數(shù)量的斷言被調(diào)用。 否則,履行的 Promise 不會通過測試。
test('the fetch fails with an error', () => {
expect.assertions(1);
return fetchData().catch(e => expect(e).toMatch('error'));
});
.resolves
? / ?.rejects
?你也可以在 ?expect ?語句中使用? .resolves
? 匹配器,Jest 將等待此 Promise 解決。 如果 Promise 被拒絕,則測試將自動失敗。
test('the data is peanut butter', () => {
return expect(fetchData()).resolves.toBe('peanut butter');
});
一定不要忘記把整個斷言作為返回值返回?如果你忘了?return
?語句的話,在 ?fetchData
?返回的這個 promise 變更為 resolved 狀態(tài)、then() 有機會執(zhí)行之前,測試就已經(jīng)被視為已經(jīng)完成了。
如果你期望 Promise 被拒絕,請使用.rejects匹配器。它參照工程 ?.resolves
? 匹配器。 如果 Promise 被拒絕,則測試將自動失敗。
test('the fetch fails with an error', () => {
return expect(fetchData()).rejects.toMatch('error');
});
或者,你也可以在測試中使用 ?async
? 和 ?await
?。 要編寫異步測試,請在傳遞給?test
?的函數(shù)前面使用?async
?關(guān)鍵字。.例如,可以用來測試相同的 ?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');
});
在這些情況下,對于與 promise 示例使用的相同邏輯?async
?,?await
?它們是有效的語法糖。
上述的諸多形式中沒有哪個形式特別優(yōu)于其他形式,你可以在整個代碼庫中,甚至也可以在單個文件中混合使用它們。 這只取決于哪種形式更能使你的測試變得簡單。
更多建議: