useFakeTimers и асинхронный обратный вызов - PullRequest
0 голосов
/ 18 января 2019

Рассмотрим эту функцию

function startTimerWithAsyncCallback(
    firstAsyncFunction,
    secondAsyncFunction,
    thirdAsyncFunction,
    millis,
) {
    setTimeout(async () => {
        await firstAsyncFunction();
        await secondAsyncFunction();
        await thirdAsyncFunction();
    }, millis);
}

Я хочу проверить, что 3 асинхронные функции вызываются после тайм-аута с использованием тайных ложных таймеров.

test('fake timers', () => {
    jest.useFakeTimers();

    const firstAsyncFunction = jest.fn();
    const secondAsyncFunction = jest.fn();
    const thirdAsyncFunction = jest.fn();

    startTimerWithAsyncCallback(
        firstAsyncFunction,
        secondAsyncFunction,
        thirdAsyncFunction,
        1000,
    );
    jest.advanceTimersByTime(2000);

    expect(firstAsyncFunction).toHaveBeenCalled();
    expect(secondAsyncFunction).toHaveBeenCalled();  // FAILS HERE !
    expect(thirdAsyncFunction).toHaveBeenCalled();
});

В этом тесте первая асинхронная функция находится в режиме ожидания, а следующие асинхронные функции не вызываются. Я не нашел способа сказать: «дождитесь завершения обратного вызова setTimeout», прежде чем делать утверждения

Я нашел обходной путь, который заключается в восстановлении реальных таймеров и ожидании 0 миллисекунд перед подтверждением.

test('fake timers and restore real timers', async () => {
    jest.useFakeTimers();

    const firstAsyncFunction = jest.fn();
    const secondAsyncFunction = jest.fn();
    const thirdAsyncFunction = jest.fn();

    startTimerWithAsyncCallback(
        firstAsyncFunction,
        secondAsyncFunction,
        thirdAsyncFunction,
        1000,
    );
    jest.advanceTimersByTime(2000);

    expect(firstAsyncFunction).toHaveBeenCalled();
    await waitAsyncFunctionsToComplete();             // WORKAROUND
    expect(secondAsyncFunction).toHaveBeenCalled();
    expect(thirdAsyncFunction).toHaveBeenCalled();
});

async function waitAsyncFunctionsToComplete() {
    jest.useRealTimers();
    await delay(0);
    jest.useFakeTimers();
}

async function delay(ms) {
    return new Promise((resolve) => {
        setTimeout(resolve, ms);
    });
}

Есть ли более правильный способ достичь этого?

1 Ответ

0 голосов
/ 18 января 2019

Как предложил Марк Мейер в комментариях, сделать startTimerWithAsyncCallback возврат Обещания более удобным для тестирования

function startTimerWithAsyncCallback(
    firstAsyncFunction,
    secondAsyncFunction,
    thirdAsyncFunction,
    millis,
) {
    return new Promise((resolve) => {      // <==
        setTimeout(async () => {
            await firstAsyncFunction();
            await secondAsyncFunction();
            await thirdAsyncFunction();
            resolve();                     // <==
        }, millis);
    });
}


describe('Using async callbacks with timers', () => {
    test('fake timers', async () => {
        jest.useFakeTimers();

        const firstAsyncFunction = jest.fn();
        const secondAsyncFunction = jest.fn();
        const thirdAsyncFunction = jest.fn();

        const promise = startTimerWithAsyncCallback( // <==
            firstAsyncFunction,
            secondAsyncFunction,
            thirdAsyncFunction,
            1000,
        );
        jest.advanceTimersByTime(2000);

        await promise;  <==

        expect(firstAsyncFunction).toHaveBeenCalled();
        expect(secondAsyncFunction).toHaveBeenCalled();
        expect(thirdAsyncFunction).toHaveBeenCalled();
    });
});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...