反应/开玩笑/酶-等待时间不够长

时间:2019-06-12 13:57:44

标签: javascript reactjs ecmascript-6 jestjs enzyme

我有一个等待多个诺言的功能

const function = async () => {
    await function1()
    await function2()
    await function3()
}

我要测试是否调用了function3:

it(('calls function3', async () => {
    jest.spyOn(api, 'function1').mockResolvedValue({})
    jest.spyOn(api, 'function2').mockResolvedValue({})
    spy = jest.spyOn(api, 'function3')
    await function()
    expect(spy).toBeCalledTimes(1)
})

此测试失败,但是当我打电话等待很多次时:

it(('calls function3', async () => {
    jest.spyOn(api, 'function1').mockResolvedValue({})
    jest.spyOn(api, 'function2').mockResolvedValue({})
    spy = jest.spyOn(api, 'function3')
    await await await await await function()
    expect(spy).toBeCalledTimes(1)
})

测试将通过。为什么是这样?在进入下一个期望行之前,await function()是否应该解决所有的诺言?

edit:等待的函数越深,即function4,我需要的等待语句越多,但不是1到1。

3 个答案:

答案 0 :(得分:1)

将承诺排入微任务队列是一个顺序问题,我正在使用flush-promises解决同一问题。

它使用节点setImmediate将微任务队列为空时将调用的回调推送到队列。

const flushPromises = require('flush-promises');

test('flushPromises', async () => {
  let a;
  let b;

  Promise.resolve().then(() => {
    a = 1;
  }).then(() => {
    b = 2;
  })

  await flushPromises();

  expect(a).toBe(1);
  expect(b).toBe(2);
});

答案 1 :(得分:1)

现在有一个proposal in Jest具有类似runAllTimers的东西,只是为了保证。

因此,如果您想避免集成flush-promises,则可以只使用setTimeout(() => {...rest code...}, 0)。由于timeout是宏任务,因此可以确保在运行之前解决所有待处理的微任务(如promises)。

有关微任务和宏任务的更多信息:https://abc.danch.me/microtasks-macrotasks-more-on-the-event-loop-881557d7af6f

答案 2 :(得分:0)

  

await function()在进入下一个期望行之前难道没有解决所有的诺言吗?

是的,await将等待返回的Promise后再继续。

这是一个简单的工作示例:

const function1 = jest.fn().mockResolvedValue();
const function2 = jest.fn().mockResolvedValue();
const function3 = jest.fn().mockResolvedValue();

const func = async () => {
  await function1();
  await function2();
  await function3();
}

it('calls function3', async () => {
  await func();
  expect(function3).toHaveBeenCalled();  // Success!
})

如果await的等待时间没有达到预期的时间,则Promise链可能会在某个时刻断开。

以下是Promise链断裂的示例:

const function1 = jest.fn().mockResolvedValue();
const function2 = jest.fn().mockResolvedValue();
const function3 = jest.fn().mockResolvedValue();

const func = async () => {
  await function1();
  await function2();
  await function3();
}

const func2 = async () => {
  func();  // <= breaks the Promise chain
}

it('calls function3', async () => {
  await func2();
  expect(function3).toHaveBeenCalled();  // <= FAILS
})

多次调用await会将其余测试功能排入PromiseJobs队列的后面,这可以使待处理的Promise回调有机会运行... < / p>

...因此,如果将上面的破损测试更改为此,它将通过:

it('calls function3', async () => {
  await await await await func2();  // <= multiple await calls
  expect(function3).toHaveBeenCalled();  // Success...only because of multiple await calls
})

...但是真正的解决方案是找到并修复Promise链断开的位置:

const func2 = async () => {
  await func();  // <= calling await on func fixes the Promise chain
}

it('calls function3', async () => {
  await func2();
  expect(function3).toHaveBeenCalled();  // Success!
})