Jest модульный тест - Как мне вызвать через асинхронную функцию в повторяющейся функции setTimeout - PullRequest
1 голос
/ 22 марта 2019

ОБНОВЛЕНИЕ: Можно с уверенностью сказать, что это в основном дубликат Шутка: Таймер и Обещание не работают должным образом.(setTimeout и асинхронная функция)


У меня есть функция, которая повторяется после выполнения асинхронной функции.Я хочу убедиться, что jest.advanceTimersOnTime(5000) продолжает давать expect(doAsyncStuff).toHaveBeenCalledTimes(X);.

. Я заметил, что мы можем дозвониться до функции, чтобы повторить функцию, но она не "проходит" при запросе jest для продвижения таймеров.Это работает, когда вы удаляете асинхронную функцию, предшествующую ей.Похоже, мне нужно заставить doAsyncStuff «позвонить через» или решить какое-то ожидающее обещание здесь?

Функция

function repeatMe() {
    setTimeout(() => {
        doAsyncStuff.then((response) => {
            if (response) {
                console.log("I get here!");
                repeatMe();
            }
        })
    }, 5000);
}

Тест

jest.useFakeTimers();

let doAsyncStuff = jest.spyOn(updater, 'doAsyncStuff');
doAsyncStuff.mockResolvedValue(true);

repeatMe();

jest.advanceTimersByTime(5000);
expect(doAsyncStuff).toHaveBeenCalledTimes(1);

jest.advanceTimersByTime(5000);
expect(doAsyncStuff).toHaveBeenCalledTimes(2); // Failed?

Ответы [ 2 ]

1 голос
/ 22 марта 2019

Похоже, мне нужно ... разрешить какое-то ожидающее здесь обещание?

Да, именно так.


Короткий ответ:Promise обратный вызов ставится в очередь в PromiseJobs then, прикованным к Promise, возвращаемому doAsyncStuff, и способом написания теста , что обратный вызов никогда не имеет шансов на выполнениепока тест не закончится .

Чтобы исправить это, дайте возможность обратным вызовам Promise во время теста:

updater.js

export const doAsyncStuff = async () => { };

code.js

import { doAsyncStuff } from './updater';

export function repeatMe() {
  setTimeout(() => {
    doAsyncStuff().then((response) => {
      if (response) {
        console.log("I get here!");
        repeatMe();
      }
    })
  }, 5000);
}

code.test.js

import * as updater from './updater';
import { repeatMe } from './code';

test('repeatMe', async () => {
  jest.useFakeTimers();

  let doAsyncStuff = jest.spyOn(updater, 'doAsyncStuff');
  doAsyncStuff.mockResolvedValue(true);

  repeatMe();

  jest.advanceTimersByTime(5000);
  expect(doAsyncStuff).toHaveBeenCalledTimes(1);  // Success!

  await Promise.resolve();  // let callbacks in PromiseJobs run

  jest.advanceTimersByTime(5000);
  expect(doAsyncStuff).toHaveBeenCalledTimes(2);  // Success!

  await Promise.resolve();  // let callbacks in PromiseJobs run

  jest.advanceTimersByTime(5000);
  expect(doAsyncStuff).toHaveBeenCalledTimes(3);  // Success!

  // ... and so on ...
});

Полную информацию о том, что именно происходит и почему, можно найти в моем ответе здесь

0 голосов
/ 22 марта 2019

Очевидно, что устранение неполадок Jest ссылается на эту проблему: we have set the definition to happen asynchronously on the next tick of the event loop. Я предполагаю, что это относится и к событиям внутри живого кода, а не только к написанным тестам.

Выполнение jest.runAllTimers() поставит вещи в бесконечный цикл.

Добавление jest.runAllTicks() повысит галочку, поэтому вышеприведенные тесты теперь работают.

Я все еще в замешательстве, но думаю, что это ответ.

jest.advanceTimersByTime(5000);
jest.runAllTicks();
expect(doAsyncStuff).toHaveBeenCalledTimes(1);

jest.advanceTimersByTime(5000);
jest.runAllTicks();
expect(doAsyncStuff).toHaveBeenCalledTimes(2); 

https://jestjs.io/docs/en/troubleshooting#defining-tests


Также необходимо, чтобы это работало, чтобы имитировать реализацию doAsyncStuff, потому что любой асинхронный материал внутри doAsyncStuff, я думаю, попадает в очередь в тиковых событиях.

            doAsyncStuff.mockImplementation(() => {
                return new Promise.resolve();
            });
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...