Tests de código asíncrono
Es común en JavaScript ejecutar código de forma asíncrona. Si tienes código que se ejecuta de forma asíncrona, Jest debe saber cuándo ha terminado de ejecutarse el código de prueba, antes de que puede pasar a otro test. Jest tiene varias formas de manejar esto.
Promesas
Devolver una promesa desde el test, y Jest esperará a que esa promesa resuelva. Si la promesa se rechaza, la prueba fallará.
For example, let's say that fetchData
returns a promise that is supposed to resolve to the string 'peanut butter'
. Podríamos testearlo con:
test('el dato es peanut butter', () => {
return fetchData().then(data => {
expect(data).toBe('peanut butter');
});
});
Async/Await
Como alternativa, se puede usar async
y await
en tests. Para escribir un test asíncrono, sólo tienes que utilizar la palabra clave async
al frente de la función pasada al test
. Por ejemplo, puede probarse el mismo escenario de fetchData
con:
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');
}
});
Puede combinar async
y await
con resolves
o rejects
.
test('el dato es peanut butter', async () => {
await expect(fetchData()).resolves.toBe('peanut butter');
});
test('el fecth falla con un error', async () => {
await expect(fetchData()).rejects.toMatch('error');
});
En estos casos, async
y await
son simplemente una mejora sintáctica para la misma lógica usada en los ejemplos de las promesas.
Be sure to return (or await
) the promise - if you omit the return
/await
statement, your test will complete before the promise returned from fetchData
resolves or rejects.
Si esperas que una promesa se rechazada, usa el método catch
. Asegúrate de añadir expect.assertions
para verificar que un cierto número de afirmaciones están siendo llamadas. De lo contrario, una promesa cumplida no hará que el test falle.
test('the fetch fails with an error', () => {
expect.assertions(1);
return fetchData().catch(error => expect(error).toMatch('error'));
});
Callbacks
If you don't use promises, you can use callbacks. For example, let's say that fetchData
, instead of returning a promise, expects a callback, i.e. fetches some data and calls callback(null, data)
when it is complete. Quieres comprobar que devuelve 'peanut butter'
.
Por defecto, Jest da por completos los tests una vez que llegan al final de su ejecución. Esto significa que este test no funciona como estaba previsto:
// ¡No hagas esto!
test('the data is peanut butter', () => {
function callback(error, data) {
if (error) {
throw error;
}
expect(data).toBe('peanut butter');
}
fetchData(callback);
});
El problema es que el test terminará tan pronto como fetchData
finalize, antes de llamar a la función callback.
Hay una forma alternativa de test
que soluciona esto. En lugar de poner el test en una función con un argumento vacío, utilice un solo argumento llamado done
. Jest esperará hasta que se llame el callback done
antes de terminar la prueba.
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 no se llama la función done()
, la prueba fallará (por tiempo de espera excedido), que es lo que quieres que ocurra.
Si la sentencia expect
falla, lanza un error y done()
no es llamada. Si queremos ver en el registro de pruebas por qué falló, tenemos que envolver el expect
en un bloque try
y pasar el error en el bloque catch
a done
. De lo contrario, terminamos con un error de tiempo de espera no explicativo que no muestra qué valor fue recibido por expect(data)
.
Jest will throw an error, if the same test function is passed a done()
callback and returns a promise. This is done as a precaution to avoid memory leaks in your tests.
resolves
/ rejects
También puede utilizar el matcher .resolves
en su declaración de expect
y Jest esperará a que esa promesa resuelva. Si se rechaza la promesa, la prueba fallará automáticamente.
test('el dato es peanut butter', () => {
return expect(fetchData()).resolves.toBe('peanut butter');
});
Asegúrate de devolver la aserción—si omites este return
su test se completará antes de que la promesa devuelta de fetchData
sea resuelta y then() ejecutará el callback.
Si esperas que una promesa sea rechazada usa el matcher rejects
. Actúa análogamente al marcador .resolves
. Si se cumple la promesa, el test fallará automáticamente.
test('the fetch fails with an error', () => {
return expect(fetchData()).rejects.toMatch('error');
});
Ninguna de estas formas es particularmente superior a las demás, y se pueden mezclar y combinar a través de una base de código o incluso en un solo archivo. Depende del estilo que sientas que hace tus tests más sencillos.