Testing Asynchronous Code
Es común en JavaScript ejecutar código de forma asincrónica. Si tienes código que se ejecuta de forma asincrónica, Jest debe saber cuando ha terminado el código que se está probando, antes de que puede pasar a otra test. Jest tiene varias formas de manejar esto.
Callbacks #
El patrón asincrónico más común es los callbacks.
Por ejemplo, supongamos que tienes una función fetchData(callback)
que trae algunos datos y llama a callback(data)
cuando esta completada. Quieres comprobar que los datos devueltos es la cadena de texto 'peanut butter'
.
Por defecto, Jest da por completados los tests una vez que llegan al final de su ejecución. Esto significa que este test no funciona como estaba previsto:
// Don't do this! test('the data is peanut butter', () => { function callback(data) { expect(data).toBe('peanut butter'); } fetchData(callback); });
El problema es que el test terminará tan pronto como fetchData
finalize, antes de llamar a la funcion callback.
Hay una forma alternativa de test
que corrige esto. En vez 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 de done
antes de terminar la prueba.
test('the data is peanut butter', done => { function callback(data) { expect(data).toBe('peanut butter'); done(); } fetchData(callback); });
Si no se llama done()
, la prueba fallará, que es lo que quieres que ocurra.
Promesas #
Si el código utiliza promesas, hay una manera más simple de manejar pruebas asincrónicas. Devolver a una promesa de tu test, y Jest esperará esa promesa a resolver. Si se rechaza la promesa, la prueba fallará automáticamente.
Por ejemplo, digamos que fetchData
, en lugar de usar un "callback", devuelve una promesa que supuestamente resolverá a la cadena de texto 'peanut butter'
. Podríamos testearlo con:
test('the data is peanut butter', () => { expect.assertions(1); return fetchData().then(data => { expect(data).toBe('peanut butter'); }); });
Asegúrese de que devuelve la promesa - si omite esta instrucción return
, la prueba terminará antes que fetchData
complete.
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(e => expect(e).toMatch('error') ); });
.resolves
/ .rejects
#
disponible en Jest 20.0.0+ #
También puede utilizar el marcador .resolves
en tu declaración de "expect" y Jest esperará a que esa promesa resuelva. Si se rechaza la promesa, el test fallará automáticamente.
test('the data is peanut butter', () => { expect.assertions(1); return expect(fetchData()).resolves.toBe('peanut butter'); });
Asegúrate de devolver la afirmación; si se omite la esta declaración return
, el test será completado antes de que fetchData
termine.
Si esperas que una promesa sea rechazada usa el marcador .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', () => { expect.assertions(1); return expect(fetchData()).rejects.toMatch('error'); });
Async/Await #
Como alternativa, se puede usar async
y await
en tests. Para escribir un test de async, sólo tienes que utilizar la palabra clave async
al frente de la función a testear
. Por ejemplo, puede probarse el mismo escenario fetchData
con:
test('the data is peanut butter', async () => { expect.assertions(1); const data = await fetchData(); expect(data).toBe('peanut butter'); }); test('the fetch fails with an error', async () => { expect.assertions(1); try { await fetchData(); } catch (e) { expect(e).toMatch('error'); } });
Por supuesto, se puede combinar async
y await
con .resolves
o .rejects
(disponible en Jest 20.0.0+).
test('the data is peanut butter', async () => { expect.assertions(1); await expect(fetchData()).resolves.toBe('peanut butter'); }); test('the fetch fails with an error', async () => { expect.assertions(1); 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.
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. Sólo depende del estilo que haga tus tests más sencillos.