Aller au contenu principal
Version : 29.7

Tester du code asynchrone

Traduction Bêta Non Officielle

Cette page a été traduite par PageTurner AI (bêta). Non approuvée officiellement par le projet. Vous avez trouvé une erreur ? Signaler un problème →

En JavaScript, il est courant que le code s'exécute de manière asynchrone. Lorsque vous testez du code asynchrone, Jest doit savoir quand ce code a terminé son exécution avant de passer au test suivant. Jest propose plusieurs méthodes pour gérer cela.

Promesses

Retournez une promesse depuis votre test, et Jest attendra sa résolution. Si la promesse est rejetée, le test échouera.

Par exemple, supposons que fetchData retourne une promesse censée se résoudre en la chaîne 'peanut butter'. Nous pourrions la tester ainsi :

test('the data is peanut butter', () => {
return fetchData().then(data => {
expect(data).toBe('peanut butter');
});
});

Async/Await

Vous pouvez également utiliser async et await dans vos tests. Pour écrire un test asynchrone, ajoutez le mot-clé async devant la fonction passée à test. Par exemple, le même scénario avec fetchData peut être testé ainsi :

test('the data is peanut butter', async () => {
const data = await fetchData();
expect(data).toBe('peanut butter');
});

test('the fetch fails with an error', async () => {
expect.assertions(1);
try {
await fetchData();
} catch (error) {
expect(error).toMatch('error');
}
});

Vous pouvez combiner async et await avec .resolves ou .rejects.

test('the data is peanut butter', async () => {
await expect(fetchData()).resolves.toBe('peanut butter');
});

test('the fetch fails with an error', async () => {
await expect(fetchData()).rejects.toMatch('error');
});

Dans ces cas, async et await sont essentiellement du sucre syntaxique pour la même logique que l'exemple avec les promesses.

attention

Veillez à retourner (ou await) la promesse - si vous omettez l'instruction return/await, votre test se terminera avant que la promesse retournée par fetchData ne soit résolue ou rejetée.

Si vous attendez un rejet de promesse, utilisez la méthode .catch. Assurez-vous d'ajouter expect.assertions pour vérifier qu'un certain nombre d'assertions sont appelées. Sinon, une promesse résolue ne ferait pas échouer le test.

test('the fetch fails with an error', () => {
expect.assertions(1);
return fetchData().catch(error => expect(error).toMatch('error'));
});

Callbacks

Si vous n'utilisez pas de promesses, vous pouvez employer des callbacks. Par exemple, imaginons que fetchData, au lieu de retourner une promesse, attende un callback, c'est-à-dire qu'elle récupère des données et appelle callback(null, data) une fois terminée. Vous voulez tester que ces données correspondent à la chaîne 'peanut butter'.

Par défaut, les tests Jest se terminent lorsqu'ils atteignent la fin de leur exécution. Cela signifie que ce test ne fonctionnera pas comme prévu :

// Don't do this!
test('the data is peanut butter', () => {
function callback(error, data) {
if (error) {
throw error;
}
expect(data).toBe('peanut butter');
}

fetchData(callback);
});

Le problème est que le test se terminera dès que fetchData aura fini, sans jamais appeler le callback.

Il existe une variante de test pour résoudre ce problème. Au lieu de placer le test dans une fonction sans argument, utilisez un argument unique nommé done. Jest attendra que le callback done soit appelé avant de terminer le test.

test('the data is peanut butter', done => {
function callback(error, data) {
if (error) {
done(error);
return;
}
try {
expect(data).toBe('peanut butter');
done();
} catch (error) {
done(error);
}
}

fetchData(callback);
});

Si done() n'est jamais appelé, le test échouera (avec une erreur de timeout), ce qui est le comportement souhaité.

Si l'assertion expect échoue, elle lance une erreur et done() n'est pas appelé. Pour voir dans les logs pourquoi le test a échoué, il faut encapsuler expect dans un bloc try et passer l'erreur dans le bloc catch à done. Sinon, nous obtenons une erreur de timeout opaque qui ne montre pas quelle valeur a été reçue par expect(data).

attention

Jest lancera une erreur si la même fonction de test reçoit un callback done() et retourne une promesse. Cette précaution évite les fuites mémoire dans vos tests.

.resolves / .rejects

Vous pouvez aussi utiliser le matcher .resolves dans votre déclaration expect. Jest attendra alors la résolution de la promesse. Si la promesse est rejetée, le test échouera automatiquement.

test('the data is peanut butter', () => {
return expect(fetchData()).resolves.toBe('peanut butter');
});

Veillez à retourner l'assertion - si vous omettez l'instruction return, votre test se terminera avant la résolution de la promesse retournée par fetchData, empêchant then() d'exécuter le callback.

Si vous attendez un rejet de promesse, utilisez le matcher .rejects. Il fonctionne de manière analogue à .resolves. Si la promesse est résolue, le test échouera automatiquement.

test('the fetch fails with an error', () => {
return expect(fetchData()).rejects.toMatch('error');
});

Aucune de ces approches n'est particulièrement supérieure aux autres, et vous pouvez les combiner librement au sein d'une même base de code, voire dans un seul fichier. Tout dépend simplement du style qui, selon vous, rend vos tests plus simples.