Это хороший вопрос, потому что он привлекает внимание к некоторым уникальным характеристикам JavaScript и тому, как он работает под капотом.Для полной разбивки при тестировании async
кода при использовании Timer Mocks см. Мой ответ здесь .
По этому вопросу важно отметить, что Таймеры Mock заменяют функции типа setTimeout
на mocks , которые помнят, как они были вызваны.Затем, когда вызывается jest.advanceTimersByTime
(или jest.runTimersToTime
для Jest
<22.0.0), <code>Jest запускает все, что было бы выполнено за прошедшее время.
Обратите внимание, чтоsetTimeout
обычно планирует сообщение для очереди сообщений JavaScript, но Timer Mocks меняет это так, чтобы все выполнялось в текущем выполняемом сообщении .
С другой стороны, когда Promise
разрешает или отклоняет, обратный вызов назначается в очередь Promise Jobs , которая запускается после завершения текущего сообщения и до начала следующего сообщения .
Так что любой текущийвыполнение синхронного кода завершится до того, как любой из обратных вызовов Promise
сможет запустить.
Так что в этом случае вам нужно позвонить jest.advanceTimersByTime
(или jest.runTimersToTime
для Jest
<22.0.0) для выполнения вызова <code>ping, запланированного с setTimeout
.
Сложность в том, что функция ping
ставит в очередь обратный вызов в очереди заданий Promise, которая не будет выполняться до текущего синхронного сообщениязавершает.
Так йоЗатем необходимо прервать текущее синхронное сообщение, чтобы разрешить обратный вызов в очереди заданий Promise.Это проще всего сделать, сделав вашу тестовую функцию async
и вызвав await
в разрешенном Promise
, который, по сути, ставит в очередь оставшуюся часть теста в конце очереди заданий Promise, позволяя выполнить все до того, как она будет запущена первой.
Итак, чтобы свести все воедино, вашему тесту нужно чередовать, увеличивая время и позволяя обратным вызовам Promise
запускаться так:
it('Should only poll maxAttempts + 1 times', async () => { // use an async test function
jest.useFakeTimers();
const onSuccessCallback = () => 'success!';
const onFailureCallback = () => 'failed';
const getStub = sinon.stub(Axios, 'get');
getStub.rejects();
const maxAttempts = 1;
ssl.waitForSsl({
onSuccess: onSuccessCallback,
onFailure: onFailureCallback,
maxAttempts
});
for (let i = 0; i < maxAttempts; i++) {
jest.advanceTimersByTime(5000); // advance the time
await Promise.resolve(); // allow queued Promise callbacks to run
}
expect(setTimeout).toHaveBeenCalledTimes(2); // SUCCESS
});