web-dev-qa-db-fra.com

Faux minuteries avec des promesses

J'ai un peu de mal à faire fonctionner correctement le framework de test Jest (version 23.2.0) lorsque j'utilise une combinaison de faux timers et de promesses. Où vais-je mal?

Disons que j'ai le module suivant:

// timing.js

export const timeout = ms =>
  new Promise(resolve => {
    setTimeout(resolve, ms)
  })

Et mon fichier de test ressemble à:

// timing.test.js

import { timeout } from './timing'

describe('timeout()', () => {
  beforeEach(() => {
    jest.useFakeTimers()
  })

  it('resolves in a given amount of time', () => {
    const spy = jest.fn()

    timeout(100).then(spy)
    expect(spy).not.toHaveBeenCalled()

    jest.advanceTimersByTime(100)
    expect(spy).toHaveBeenCalled()
  })
})

Cela échoue avec la sortie suivante:

● timeout › resolves in a given amount of time

expect(jest.fn()).toHaveBeenCalled()

Expected mock function to have been called, but it was not called.

  15 |
  16 |     jest.advanceTimersByTime(100)
> 17 |     expect(spy).toHaveBeenCalled()
     |                 ^
  18 |   })
  19 | })
  20 |

  at Object.<anonymous> (src/timing.test.js:17:17)

Cependant, si je retire la promesse:

// timing.js
export const timeout = ms => ({
  then: resolve => {
    setTimeout(resolve, ms)
  }
})

... le test passera

timeout
  ✓ resolves in a given amount of time (5ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        1.304s

MISE À JOUR

Bien que ce ne soit pas la solution la plus élégante, j'utilise actuellement le test ci-dessous. Cela fonctionne, mais je suis toujours intrigué pourquoi l'original n'a pas

import { timeout } from './timing'

describe('timeout', () => {
  it('resolves in a given amount of time', done => {
    setTimeout(() => done(new Error('it didn\'t resolve or took longer than expected')), 10)
    return timeout(9).then(done)
  })
})
13
johngeorgewright

Pour le moment, ce n'est pas pris en charge

Vous ne faites rien de mal - cela ne fonctionne pas pour le moment - désolé. Les choses suivantes doivent se produire avant que cela fonctionne de notre côté:

  • Jest doit fusionner le travail en cours pour fusionner lolex en tant que mise en œuvre de faux temporisateur ici https://github.com/facebook/jest/pull/5171
  • Lolex doit soutenir le pompage à travers ses promesses - nous en avons discuté avec l'équipe V8 lors d'un récent sommet des collaborateurs de Node.js. Cela exposerait un crochet que nous utiliserons pour permettre de faire quelque chose comme advanceTimeByTime(100) et que cela fonctionnerait avec des promesses.

Le problème dans un Gist est que la .then(spy) n'est appelée que plus tard .

Comme nous sommes bénévoles - il n'y a pas de calendrier concret pour ces choses. J'espère que SimenB fera la fusion dans les 2-3 prochains mois et je poursuivrai avec le crochet avec l'équipe V8 le mois prochain.

Que pouvez-vous faire maintenant

Vous pouvez toujours écrire un test asynchrone:

// note this is an async function now
it('resolves in a given amount of time', async () => {
  // this is in a promise.reoslve.then to not 'lock' on the await
  Promise.resolve().then(() => jest.advanceTimersByTime(100));
  await timeout(100);
});

Vous pouvez ajouter des attentes après le délai d'attente si vous souhaitez attendre autre chose.

15
Benjamin Gruenbaum