写在前面:
在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'); });
写在最后:
在这些情况下,异步和等待实际上只是与承诺示例使用的相同逻辑的语法糖。
这几种方法中没有一个特别优于另外一个,你可以将它们组合在一个代码库中,甚至可以在单个文件中进行匹配。它只是取决于哪种样式使你的测试更简单。
系列教程:
1. 前端测试框架Jest系列教程 -- Matchers(匹配器)
2.前端测试框架Jest系列教程 -- Asynchronous(测试异步代码)