Jest 解析之测试异步代码

631 阅读3分钟

Javascript 开发中经常会出现异步处理的场景,Jest 需要知道异步代码什么时候执行完,才能再去执行下一个测试用例,下面会介绍几种处理方式。

Promises

如果需要进行 resolved 断言,可以使用 then 方法,根据回调的执行结果进行断言。这个时候断言正确就会通过,promise 返回 rejected 的话也会出现错误提示。

如果需要进行 rejected 断言,可以使用 catch 方法,在方法的回调函数中执行断言。断言正确就会通过,promise 返回 resolved 的话也会通过。这里就存在一个问题,因为这个时候不会执行 catch 方法里面的断言,而是直接判断为通过。为了解决这个问题,可以增加代码 expect.assertions(1) 来判断 catch 里面的断言是否会执行。

function resolvedFetchData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve("leon"), 10);
  });
}
function rejectedFetchData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => reject("ming"), 10);
  });
}
test("fetch data", () => {
  return resolvedFetchData().then((data) => {
    expect(data).toBe("leon");
  });
});

test("fetch data no asserations", () => {
  return rejectedFetchData().catch((err) => {
    expect(err).toBe("ming");
  });
});

test("fetch data with asserations", () => {
  expect.assertions(1);
  return rejectedFetchData().catch((err) => {
    expect(err).toBe("ming");
  });
});

在上面的例子中,"fetch data" 用于断言正确的结果,"fetch data no asserations" 用于断言错误的结果,但是会出现上面的提到的问题, promise 返回 resolved 结果的时候不会执行 catch 回调但是会出现“断言正确”的错误结果,而 "fetch data with asserations" 就是改良后的测试用例。

Async/Await

类似的,可以写出 async...await 方式的断言代码

function resolvedFetchData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve("leon"), 10);
  });
}
function rejectedFetchData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => reject("ming"), 10);
  });
}
test("fetch data", async () => {
  const data = await resolvedFetchData();
  expect(data).toBe("leon");
});

test("fetch data with asserations", async () => {
  expect.assertions(1);
  try {
    await rejectedFetchData();
  } catch (err) {
    expect(err).toBe("ming");
  }
});

在上面的例子中 “fetch data” 例子用于断言正常的结果,“fetch data with asserations” 用于断言异常的结果。

但是,这样编写测试代码显然比较繁琐,于是出现了结合 resolves/rejects 匹配器的新方式

test("new fetch data", async () => {
  await expect(resolvedFetchData()).resolves.toBe("leon");
});

test("new fetch data with asserations", async () => {
  await expect(rejectedFetchData()).rejects.toBe("ming");
});

相比上面的例子,新方式明显简化了代码,对于正常和异常的结果断言只用了一行代码进行处理。

Callbacks

除了可以使用 promise 编写异步代码,还可以使用回调函数。特别的是,编写测试用例的时候 test 的第二个参数里面,需要提供 done 回调函数,这样 jest 就会等待 done 函数被调用才终止测试,超时也会报错。如果没有 done 回调函数就会直接执行里面的同步代码,不能进行异步结果断言。

function fetchData(fn) {
  setTimeout(() => {
    fn(undefined, "leon");
  }, 10);
}

test("fetch data", (done) => {
  function callback(err, data) {
    if (err) {
      done(err);
      return;
    }
    try {
      expect(data).toBe("leon");
      done();
    } catch (err) {
      done(err);
    }
  }

  fetchData(callback);
});

上面的例子中,fetchData 需要等到 10ms 之后才会调用提供的回调函数,编写测试用例的时候就需要增加参数 done。由于 expect 断言的时候会出现错误情况,这个时候避免等到超时才停止测试用例,可以使用 try...catch 去捕获异常。

小结

本文主要讲了 promise,async...await 和回调函数三种异步代码测试的情况,对 promise 代码进行测试的时候需要注意断言异常情况的时候可能会出现 promise 执行返回 fulfilled 的情况,对 async...await 进行测试的时候可以结合 resolves 和 rejectes 匹配器精简代码,对回调函数进行测试的时候需要用到 done 函数来控制 jest 代码的执行时机。

参考资料:jestjs.io/docs/asynch…