Le rappel asynchrone n'a pas été invoqué dans le délai d'expiration de 5000 ms spécifié par jest.setTimeout

238

J'utilise marionnettiste et plaisanterie pour exécuter des tests frontaux.

Mes tests se présentent comme suit:

describe("Profile Tab Exists and Clickable: /settings/user", () => {
    test(`Assert that you can click the profile tab`, async () => {
      await page.waitForSelector(PROFILE.TAB);
      await page.click(PROFILE.TAB);
    }, 30000);
});

Parfois, lorsque je lance les tests, tout fonctionne comme prévu. D'autres fois, j'obtiens une erreur:

Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.

      at node_modules/jest-jasmine2/build/queue_runner.js:68:21
      at Timeout.callback [as _onTimeout] (node_modules/jsdom/lib/jsdom/browser/Window.js:633:19)

C'est étrange car:

  1. J'ai spécifié que le délai d'expiration était de 30000

  2. Que j'obtienne ou non cette erreur est apparemment très aléatoire

Quelqu'un peut-il deviner pourquoi cela se produit?

Asool
la source
Quelle ligne expire?
lloyd
@Asool Pourriez-vous fournir un dépôt GitHub? Il nous sera plus facile et plus rapide de vous fournir une solution. :)
Shishir Anshuman
@Asool, tout commentaire sur la réponse que j'ai postée
Tarun Lalwani
1
pourrait-il être que le test échoue réellement pour les 30000 ms mais l'erreur de plaisanterie n'inclut tout simplement pas la valeur que vous avez passée? ce qui signifie, si vous mettez un délai de 0 ms, est-ce que l'erreur de plaisanterie change?
Nirit Levi
J'ai vu cette erreur lorsque je déboguais mes tests. Un arrêt à un point d'arrêt a provoqué cette erreur
Neets

Réponses:

261

Par conséquent, le délai d'expiration que vous spécifiez ici doit être plus court que le délai d'expiration par défaut.

Le délai d'expiration par défaut est 5000et le cadre par défaut est jasmineen cas de jest. Vous pouvez spécifier le délai d'expiration à l'intérieur du test en ajoutant

jest.setTimeout(30000);

Mais cela serait spécifique au test. Ou vous pouvez configurer le fichier de configuration pour le framework.

https://facebook.github.io/jest/docs/en/configuration.html#setuptestframeworkscriptfile-string

// jest.config.js
module.exports = {
  // setupTestFrameworkScriptFile has been deprecated in
  // favor of setupFilesAfterEnv in jest 24
  setupFilesAfterEnv: ['./jest.setup.js']
}

// jest.setup.js
jest.setTimeout(30000)

Voir aussi ce fil

https://github.com/facebook/jest/issues/5055

https://github.com/facebook/jest/issues/652

PS faute d'orthographe setupFilesAfterEnv(c'est-à-dire setupFileAfterEnv) lancera également la même erreur.

Tarun Lalwani
la source
2
Merci d'avoir répondu à une question que je n'ai pas pu trouver facilement via la documentation Jest.
HartleySan
21
Comme cela m'a aidé, il convient de noter que le setupTestFrameworkScriptFilea été remplacé par setupFilesAfterEnv, il devient doncsetupFilesAfterEnv: ["./jest.setup.js"]
Maxim Geerinck
1
J'ai aussi trouvé que cela jest.setTimeout(10000)pouvait être ajouté à un seul test pour un cas de bord afin que la configuration entière n'ait pas besoin de changer :)
James
Je manquer quelque chose , mais si j'ajoute jest.setTimeout(30000);dans jest.config.jsje reçois « ReferenceError: plaisanterie n'est pas défini ». J'ai essayé d'ajouter const jest = require("jest");mais je reçois "TypeError: jest.setTimeout n'est pas une fonction".
Jean Paul
Oups, j'ai lu trop vite: l' setupFilesAfterEnvargument dans jest.config.jspointera vers un autre fichier où nous avons mis l' jest.setTimeout(30000)option. C'est bien que nous puissions configurer cela mais cela me semble un peu compliqué cependant.
Jean Paul
64

Il doit appeler le async/awaitquand il est asynchrone du test.

describe("Profile Tab Exists and Clickable: /settings/user", () => {
    test(`Assert that you can click the profile tab`, async (done) => {
        await page.waitForSelector(PROFILE.TAB);
        await page.click(PROFILE.TAB);
        done();
    }, 30000);
});
le code de schrodinger
la source
24
Pourquoi devrions-nous avoir doneune fonction asynchrone? Ne retournons-nous pas simplement Promise ou indéfini?
Charlie Schliesser
2
Non, ce n'est pas correct. Vous n'avez pas besoin d'appeler done () car vous attendez vos promesses ou vous pouvez simplement revenir page.click. done () est utilisé, au moins dans mon cas, principalement pour les tests avec des rappels.
Justin
2
Merci les gars, j'ai supprimé le donerappel qui n'est pas nécessaire.
le code de schrodinger
26
n'est-ce pas le même code que dans la question d'origine maintenant?
Joe
1
La présence d'un paramètre (nommé donedans ce cas) dans le rappel fait attendre Jest jusqu'à ce que ce paramètre soit appelé. Sa présence est importante même si elle n'est pas utilisée.
vaughan
54

La réponse à cette question a changé avec l'évolution de Jest. Réponse actuelle (mars 2019):

  1. Vous pouvez remplacer le délai d'expiration d'un test individuel en ajoutant un troisième paramètre au it. c'est à dire.it('runs slow', () => {...}, 9999)

  2. Vous pouvez modifier la valeur par défaut à l'aide de jest.setTimeout. Pour faire ça:

 // config
   "setupFilesAfterEnv": [  // NOT setupFiles
     "./src/jest/defaultTimeout.js"
   ],

et

// File: src/jest/defaultTimeout.js
/* global jest */
jest.setTimeout(1000)
  1. Comme d'autres l'ont noté, et qui ne sont pas directement liés à cela, donen'est pas nécessaire avec l'approche asynchrone / attente.
ndp
la source
5
c'est la version la plus moderne
jonashdown
23

Je voudrais ajouter (c'est un peu long pour un commentaire) que même avec un timeout de 3000mes tests échouerait parfois (au hasard) avec

Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.

Grâce à l'excellente réponse de @ Tarun, je pense que le moyen le plus court de corriger beaucoup de tests est:

describe('puppeteer tests', () => {
  beforeEach(() => {
    jest.setTimeout(10000);
  });

  test('best jest test fest', async () => {
    // blah
  });
});
romain
la source
9
Vous n'avez pas besoin d'appeler à l' jest.setTimeout()intérieur beforeEach, l'appeler une fois suffit pour tous les tests.
Marcos Pereira
19

Il s'agit d'une mise à jour relativement nouvelle, mais elle est beaucoup plus simple. Si vous utilisez jest 24.9.0 ou supérieur, vous pouvez simplement ajouter testTimeoutà votre configuration:

// in jest.config.js
module.exports = {
  testTimeout: 30000
}
e-shfiyut
la source
17

Assurez-vous d'invoquer des done();rappels ou il ne passera pas simplement le test.

beforeAll((done /* call it or remove it*/) => {
  done(); // calling it
});

S'applique à toutes les autres fonctions qui ont un rappel done ().

ZenVentzi
la source
1
Bien mentionné, @ZenVentzi. Merci :)!
ivanleoncz
11

Pour jest 24.9+, vous pouvez également définir un délai d'expiration à partir de la ligne de commande en ajoutant --testTimeout

Voici un extrait de ses documents

--testTimeout=<number>
Default timeout of a test in milliseconds. Default value: 5000.
Monsieur 14
la source
3

J'ai récemment rencontré ce problème pour une autre raison: j'exécutais des tests de manière synchrone jest -i, et cela allait juste expirer. Quel que soit le raisonnement, exécuter les mêmes tests en utilisant jest --runInBand(même si-i est censé être un alias) n'expire pas.

Peut-être que cela aidera quelqu'un ¯\_(:/)_/¯

Jona
la source
1

Le problème de délai d'attente se produit lorsque le réseau est lent ou que de nombreux appels réseau sont effectués à l'aide de await, ces scénarios dépassent le délai par défaut, c'est-à-dire 5000 ms. Pour éviter l'erreur d'expiration, augmentez simplement le délai d'expiration des globaux qui prennent en charge un délai d'expiration. Une liste des mondiaux et leur signature peut être trouvée ici .
Pour Jest 24.9

Neeraj Sewani
la source
1
// in jest.setup.js
jest.setTimeout(30000)

Si sur Jest <= 23:

// in jest.config.js
module.exports = {
  setupTestFrameworkScriptFile: './jest.setup.js'
}

Si Jest> 23:

// in jest.config.js
module.exports = {
  setupFilesAfterEnv: ['./jest.setup.js']
}
alexrogères
la source
0

Dans le cas où quelqu'un ne résout pas le problème, utilisez les méthodes ci-dessus, j'ai corrigé le mien en entourant la fonction asynchrone par une fonction de flèche. Un péché:

describe("Profile Tab Exists and Clickable: /settings/user", () => {
    test(`Assert that you can click the profile tab`, (() => {
      async () => {
        await page.waitForSelector(PROFILE.TAB)
        await page.click(PROFILE.TAB)
      }
    })(), 30000);
});
suga_e
la source
1
Il me semble que placer la fonction de flèche autour de l'async ne dira pas au test d'attendre la fin du test, donc même si vous n'obtenez pas d'erreur maintenant, vous aurez un test exécuté en dehors de son thread et a) le toute la suite de tests peut se terminer avant la fin de ce test, ne pas tester ce code et b) de futures erreurs à l'intérieur de ce test pourraient apparaître lors d'un autre test dans la suite, rendant vos tests floconneux et difficiles à maintenir.
Mary Shaw
0

Dans mon cas, cette erreur a commencé à apparaître de manière aléatoire et ne disparaîtrait pas même après avoir défini un délai d'expiration de 30000. La simple fin du processus dans le terminal et la réexécution des tests ont résolu le problème pour moi. J'ai également supprimé le délai d'attente et les tests réussissent encore.

Saurabh Misra
la source
-2

Dans Node ... ce que je vois que les gens ont fait comme exemple est ci-dessous, en utilisant fakeEventEmitter

import { EventEmitter } from 'events';
describe('your case', () => {
 let fakeEventEmitter: EventEmitter;
 beforeEach(async () => {
   fakeEventEmitter = new EventEmitter();
   (fakeEventEmitter as any).pid = 123;
 }),
 it('should do something you want to do', done => {
            anAsynchronouseFunction(testOptions, context).subscribe({
                complete: () => {
                    expect(something).toBeTruthy();
                    done();
                }
            });
            fakeEventEmitter.emit('exit', 0);
        });
});
WickStargazer
la source