React / Jest / Enzyme - ждите не долго - PullRequest
0 голосов
/ 12 июня 2019

У меня есть функция, которая ожидает несколько обещаний

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

Я хочу проверить, что функция3 называется:

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: Чем глубже ожидаемая функция, т. Е. Функция4, тем больше ожидающих операторов мне нужно, но не от 1 до 1.

Ответы [ 3 ]

1 голос
/ 13 июня 2019

К настоящему времени в Jest есть предложение , имеющее что-то вроде runAllTimers, но для обещаний.

Так что, если вы хотите избежать интеграции flush-promises, вы можете просто использовать setTimeout(() => {...rest code...}, 0). Поскольку timeout является макрозадачей, перед выполнением этой операции гарантируется, что все ожидающие выполнения микрозадачи (например, обещания) будут решены.

Подробнее о микрозадачах и макрозадачах: https://abc.danch.me/microtasks-macrotasks-more-on-the-event-loop-881557d7af6f

1 голос
/ 12 июня 2019

Это вопрос порядка, в котором обещания помещаются в очередь в очереди микро-задач, я использую 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);
});
0 голосов
/ 14 июня 2019

Разве 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 обратным вызовам шанс на запуск ...

... поэтому пройденный выше тест пройдёт, если он будет изменён на этот:

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!
})
...