Javascript 异步函数的行为与我对Jest的预期不同
Javascript Jest测试框架说,我需要在回调中添加Javascript 异步函数的行为与我对Jest的预期不同,javascript,asynchronous,jestjs,Javascript,Asynchronous,Jestjs,Javascript Jest测试框架说,我需要在回调中添加done(),以测试异步函数,否则函数将在测试完成后返回,因此测试将失败。我已将Jest添加到我的package.json中,并添加了以下两个文件: src.js: function fetchData(cb) { setTimeout(cb, 2000, 'peanut butter') } module.exports = fetchData function fetchData(cb) { setTimeout(cb,
done()
,以测试异步函数,否则函数将在测试完成后返回,因此测试将失败。我已将Jest添加到我的package.json中,并添加了以下两个文件:
src.js:
function fetchData(cb) {
setTimeout(cb, 2000, 'peanut butter')
}
module.exports = fetchData
function fetchData(cb) {
setTimeout(cb, 2000, 'peanut')
}
module.exports = fetchData
src.test.js:
const fetchData = require('./src')
test('the data is peanut butter', () => {
function callback(data) {
expect(data).toBe('peanut butter')
// no done() method!
}
fetchData(callback);
})
const fetchData = require('./src')
test('the data is peanut butter', () => {
function callback(data) {
expect(data).toBe('peanut butter')
done()
}
fetchData(callback);
})
我用上面的代码通过了测试,但我认为我应该得到一个失败的测试,因为我的测试文件中没有done()
。我的fetchData()
方法不是异步的吗
编辑:根据尼古拉斯的回答,我将代码更改为: src.js:
function fetchData(cb) {
setTimeout(cb, 2000, 'peanut butter')
}
module.exports = fetchData
function fetchData(cb) {
setTimeout(cb, 2000, 'peanut')
}
module.exports = fetchData
src.test.js:
const fetchData = require('./src')
test('the data is peanut butter', () => {
function callback(data) {
expect(data).toBe('peanut butter')
// no done() method!
}
fetchData(callback);
})
const fetchData = require('./src')
test('the data is peanut butter', () => {
function callback(data) {
expect(data).toBe('peanut butter')
done()
}
fetchData(callback);
})
测试运行者应根据Jest文档评估期望/断言,并失败(花生酱通过,花生酱预期),但仍显示通过测试
此外,Jest文档还指出:
If done() is never called, the test will fail, which is what you want to happen
测试通过回调中有和没有
done()
方法,以及有和没有传递给回调的正确(花生酱
)参数(即,所有四个变量都通过)。如果没有作为参数传递done
,测试运行程序认为代码是同步的,不会等待特殊的事情发生——在本例中,调用done
因此,在您的断言/期望有机会运行之前,测试被标记为成功。您的
fetchData
本身是异步的,Jest对此没有影响。另一方面,Jest需要知道测试何时完成,通常是测试函数退出的时候,但这只包括同步代码。在您的例子中,当您的测试函数退出时,您没有调用任何断言,Jest认为这是成功的(因为没有失败)
为了证明没有调用断言,我将使用仅当它完全调用number
断言时才通过测试。例如,如果在示例中将其设置为1:
test('the data is peanut butter', () => {
// Require exactly 1 assertion to pass the test
expect.assertions(1)
function callback(data) {
expect(data).toBe('peanut butter')
}
fetchData(callback)
})
您将看到错误消息:
● the data is peanut butter
expect.assertions(1)
Expected one assertion to be called but received zero assertion calls.
如您所见,没有调用断言。除了演示目的之外,您还可以使用expect.assertions
来确保在未调用断言时测试失败
对于任何异步任务,都需要让Jest知道测试是异步的,并且需要在测试完成时通知Jest。一种方法是使用done
回调。当您的测试函数接受一个参数(通常称为done
)时,Jest会将其视为异步测试,并等待您调用done()
回调,或者当达到超时阈值时(默认值:5s)。在已编辑的示例中,函数接受零参数,对done()
的调用不是Jest的回调
test('done callback', done => {
// ^^^^ 1 argument
// Jest waits until this callback is called or the timeout was reached.
// ...
})
test('no callback argument', () => {
// ^^ no argument
// Test finishes as soon as the function exits
// ...
})
这已经很少使用了,因为现在承诺更为常见,而且它们使这变得更为简单,因为您可以返回承诺,而Jest将等待其完成,而无需任何额外设置。此外,您还可以使用async
和await
使其更加完善,因为它是承诺之上的语法糖
另请参见和如果未将
done
作为参数传递,则代码将被视为同步且不等待。在断言有机会运行之前,测试被认为是成功的。无论我设置了多长时间的计时器,测试运行者都会等待计时器完成,然后在控制台中报告测试结果。但是文档说我需要做一些特殊的事情来处理异步代码,而且Expection对象肯定是异步执行的(作为对setTimeout()
的回调),测试运行程序(或者更具体地说是节点本身)在终止进程之前等待最后注册的setTimeout
回调过期。尝试创建一个空文件,只在其中放入一个setTimeout
。您将看到,它将不会退出进程,直到FiresTest有一个总的gotcha,在那里它修改了计时函数的行为。我对我的答案进行了编辑,以包括一个工作示例和对讨论该行为的问题的引用。同样,在您包含的编辑中,您没有将done
作为参数传递给test
<代码>完成除了在完成测试时被调用外,还应作为参数传递。看看我的例子,运行它,你就会明白重点。请注意第8行,其中我通过了test
作为一个参数,如果您通过但未调用done
测试,则测试应在一段时间后超时。在我使用的Mocha中,如果未调用done
,则默认情况下2000毫秒后超时。在我的书中,编写这些本机函数是一个很大的禁忌。他们本可以创建自己的计时功能,这样可以避免一半的问题和我们的时间。至于done从不被调用,如果测试失败
,这只适用于您实际通过if done
作为参数的情况,而您在任何示例中都没有这样做。希望helpsJest在默认情况下不会覆盖计时器,但它提供了jest.useFakeTimers()
来模拟计时器进行手动(细粒度)控制。请参见。在我的示例中,setTimeout
是如何来的呢?它直接在规范文件中声明的,除非我使用useFakeTimers()
/runAllTimers()
?@NicholasKyriakides在您的示例中,expect(data).toBe('foo-butter')
断言失败,从而抛出done()
未到达第行。正是因为Jest不会劫持setTimeout
(除非您使用Jest.uesFakeTimers
),所以它不知道它抛出了什么。解决方法如中所示。承诺不会出现此问题,因为如果断言抛出。然后
,回调,承诺将被拒绝,并指示失败