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 代码的执行时机。