1. 程式人生 > >前端測試框架Jest系列教程 -- Asynchronous(測試非同步程式碼)

前端測試框架Jest系列教程 -- Asynchronous(測試非同步程式碼)

寫在前面:

  在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');
});

寫在最後:

在這些情況下,非同步和等待實際上只是與承諾示例使用的相同邏輯的語法糖。


這幾種方法中沒有一個特別優於另外一個,你可以將它們組合在一個程式碼庫中,甚至可以在單個檔案中進行匹配。它只是取決於哪種樣式使你的測試更簡單。

系列教程: