Jest - 测试异步代码

2019-08-22  本文已影响0人  小黄人get徐先生

代码异步运行在 JavaScript 里很常见。当你有异步代码运行时,Jest 需要知道它正在测试的代码何时完成,然后才能运行另外一个测试。Jest 有几种方法处理这种情况。

Callbacks

常见的异步模式就是回调。

例如,你有一个 fetchData(callback) 函数拉取一些数据并在它完成的时候调用 callback(data)。你想要测试这个返回的数据是字符串 peanut butter

默认情况下,Jest测试在执行结束时就完成了(调用是异步的,但此时测试已经结束)。这意味着该测试将无法正常工作:

// Don't do this!
test('the data is peanut butter', () => {
  function callback(data) {
    expect(data).toBe('peanut butter');
  }

  fetchData(callback);
});

问题是,一旦 fetchData 完成,测试就会在调用回调之前完成。

有一种替代形式的 test可以解决这个问题。不要将测试放在带有空参数的函数中,而是使用一个名为done的参数。Jest将等到调用done回调函数后才完成测试。(调用 done 回调函数的时候已经发生了断言)

test('the data is peanut butter', done => {
  function callback(data) {
    expect(data).toBe('peanut butter');
    done();
  }

  fetchData(callback);
});

如果 done() 没有被调用,这个测试将失败,这也是你所期望看到的。

Promises

如果你的代码返回 promises,这里有一个简单的方式处理异步测试。Jest 从测试中返回一个 promise,并且 Jest 将等待 promise 被 resolve。如果这个 promise 被 rejected,这个测试将自动失败。

例如,我们看看 fetchData,代替使用回调,而是返回一个 promise,该 promise 被 resolve 为字符串 ’peanut butter‘。我们如下测试:

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

确保返回 promise - 如果你省略了 return 语句,你的测试将在 fetchData resolves 之前完成,然后 then() 才有机会执行回调。

如果你希望一个 promise 被 rejected,可以使用 .catch 方法。确保添加 expect.assertions 来验证调用了一定数量的断言。否则,一个 fulfilled 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 resolve。如果这个 promise rejected,这个测试将自动失败。

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

确保返回断言 - 如果你省略 return 语句,那么测试将在解析fetchData返回的承诺之前完成,then() 才有机会执行回调。

如果你期望一个 promise 被 rejected,则使用 .rejects 匹配器。工作原理和 .resolves 类似。如果 promise 是 fulfilled,这个测试将自动失败。

test('the fetch fails with an error', () => {
  return expect(fetchData()).rejects.toMatch('error');
});
Async / Await

或者,你可以在你的测试中使用 asyncawait。只需在传递给测试的函数前面使用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 (e) {
    expect(e).toMatch('error');
  }
});

当然。你可以用 .resolves.rejects 组合 async 和 await 。

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

在这些案例中,asyncawait 都非常有效,就像语法糖一样,实现了与 promises 例子相同的逻辑。

上一篇 下一篇

猜你喜欢

热点阅读