测试异步代码

JS 中会用到很多异步编程。当测试异步代码时,Jest 需要知道代码何时结束,然后它才会继续执行另一条测试用例。Jest 有很多方法处理异步编程。

回调函数

最常见的异步编程莫过于回调函数。

比如,你有一个 fetchData(callback) 函数,获取一些数据,完成后调用 callback(data)。你想检测返回的 data 刚好是字符串 peanut butter

默认情况下,Jest 一旦执行完成,测试用例就结束了。这意味着如下代码不会如你所愿:

// DO NOT do this
test('the data is peanut butter', () => {
  function callback(data) {
    expect(data).toBe('peanut butter 2')
  }

  fetchData(callback)
})

问题在于测试用例会立即结束,来不及调用回调函数。

可以使用另一种形式的 test 修复它。即在 test 回调函数中传入 done 参数。Jest 会等待 done 被执行,才会真正结束测试用例。

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

  fetchData(callback)
})

若没有调用 done,测试用例会失败,如你所愿。

Promises

若代码中含有 Promise,在异步测试中有更简单的方法。只需在 test 中返回一个 promise,Jest 会等待 Promise 结束。如果 Promise 被拒,测试自动失败。

比如,fetchData 使用 Promise ,成功后返回字符串 peanut butter。我们可以如此测试:

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

一定要返回 Promise --- 如果忘记 return 语句,测试就会在 fetchData 完成前结束。

如果期望使用 .catch 函数捕捉被拒的 promise,一定要使用 expect.assertions 声明期望的断言数量。否则,成功通过的 Promise 将无法使测试用例失败。

test('the fetch fails with an error', () => {
  // 提前声明期望的断言数量
  expect.assertions(1)
  return fetchData().catch(e => expect(e).toMatch('error'))
})

.resolves / .rejects

Jest 20.0.0+ 有效

还可以在 expect 语句中使用 .resolves 匹配器,Jest 会等待该 promise 完成。如果它被拒,测试自动失败。

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

记得要返回断言 --- 如果忽略了 return 语句,测试将在 fetchData 完成前结束。

若期望 promise 被拒,可用 .rejects 匹配器,它和 .resolves 类似。如果 promise 完成,测试用例自动失败。

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

Async/Await

你也可以在测试代码中使用 asyncawait。编写 async 测试,只需在 test 回调函数前增加 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')
})

如上代码中,async/await 不过语法糖,实现了 promise 同样逻辑。

以上异步风格没有优劣之分,哪种方式让代码最简单,就选择哪种。

REF

results matching ""

    No results matching ""