78 votes

Jest: Timer et Promise ne fonctionnent pas bien. (fonction setTimeout et async)

Des idées sur ce code

 jest.useFakeTimers() 

it('simpleTimer', async () => {
  async function simpleTimer(callback) {
    await callback()    // LINE-A without await here, test works as expected.
    setTimeout(() => {
      simpleTimer(callback)
    }, 1000)
  }

  const callback = jest.fn()
  await simpleTimer(callback)
  jest.advanceTimersByTime(8000)
  expect(callback).toHaveBeenCalledTimes(9)
}

''

Échec avec

 Expected mock function to have been called nine times, but it was called two times.

Cependant, si je supprime await de LINE-A, le test réussit.

Est-ce que Promise et Timer ne fonctionnent pas bien?

Je pense que la raison peut-être que la plaisanterie attend la deuxième promesse pour se résoudre.

3voto

nemo Points 680

Il y a un cas d'utilisation auquel je n'ai tout simplement pas pu trouver de solution:

 function action(){
  return new Promise(function(resolve, reject){
    let poll
    (function run(){
      callAPI().then(function(resp){
        if (resp.completed) {
          resolve(response)
          return
        }
        poll = setTimeout(run, 100)
      })
    })()
  })
}

Et le test ressemble à:

 jest.useFakeTimers()
const promise = action()
// jest.advanceTimersByTime(1000) // this won't work because the timer is not created
await expect(promise).resolves.toEqual(({completed:true})
// jest.advanceTimersByTime(1000) // this won't work either because the promise will never resolve

Fondamentalement, l'action ne se résoudra que si le chronomètre avance. On dirait une dépendance circulaire ici: la promesse a besoin d'une minuterie pour avancer pour se résoudre, une fausse minuterie a besoin d'une promesse pour se résoudre pour avancer.

0voto

mummybot Points 195

Ce qui précède était vraiment utile! Pour ceux qui essaient de le faire avec les hooks React (!), Le code suivant a fonctionné pour nous:

 // hook
export const useApi = () => {
  const apis = useCallback(
    async () => {
      await Promise.all([
        new Promise((resolve) => {
          api().then(resolve);
        }),
        new Promise((resolve) => {
          return setTimeout(() => {
            resolve();
          }, 10000);
        }),
      ]);
    },
    [],
  );
  return [apis];
}

// test
import { renderHook, act } from '@testing-library/react-hooks';
function flushPromises() {
  return new Promise((resolve) => setImmediate(resolve))
}

it('tests useApi', async () => {
  jest.useFakeTimers();
  const { result } = renderHook(() => useApi());
  api.mockReturnValue(Promise.resolve());
  await act(async () => {
    const promise = result.current[0]()
    await flushPromises()
    jest.runAllTimers()

    return promise
  })
});

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X