Bienvenidos sean a este post, hoy veremos unos test mas reales.
En los ultimos tiempos la programacion web, especialmente javascript, tiende a trabajar de forma asincronica. Esto implica que no tenemos un control de cuando un callback sera invocado, una promesa resuelta, asi como tambien podemos quedar a la espera de un evento. Esto puede derivar en serios inconvenientes para trabajar con nuestros tests. Para trabajar con esto vamos a necesitar el proyecto que creamos en este post, sino lo poseen les dejo un link para descargarlo:
Simplemente extraigan el directorio en el PC y ya esta listo para ser usado. Nuestro primer paso sera ir al archivo hola.spec.ts y modificaremos el codigo de la siguientte manera:
class MockAsync {
funcionLenta( completa: (valor: string) => void) {
setTimeout(()=> {
completa("completado");
}, 1000);
}
}
describe("fallando el test asinc", () => {
it("debe esperar el callback para completar", () => {
let mockAsync = new MockAsync();
console.log("1. llamando a funcionLenta");
let devuelto !: string;
mockAsync.funcionLenta((v: sttring) => {
console.log("2. llamada completa");
devuelto = v;
});
console.log("3. chequeando valor devuelto");
expect(devuelto).toBe("completado");
});
});
La clase que definimos primero solo posee un metodo. Este metodo a su vez tiene un argumento que sera una funcion que recibe un valor de tipo string. En el bloque de esta ejecutaremos un timeout que espera 1000 ms para devolver a la funcion con el valor completado.
En el test primero crearemos un objeto de la clase anterior. Lo siguiente sera mostrar en consola un mensaje indicando que se llama al metodo de la clase. Despues declaramos un objeto que recibira lo devuelto por el metodo anterior. Lo siguiente sera llamar al metodo de la clase y en ella pasaremos una funcion anonima donde indicaremos que la llamada fue completa y en la variable anterior almacenaremos lo devuelto. Por ultimo tenemos una indicacion de que «testearemos» el valor devuelto y mediante expect pasaremos el valor de devuelto y lo comparamos con toBe donde le pasamos el texto que debe tener. Probemos el test para ver su salida:
$ npm test
> testing@1.0.0 test
> jest
console.log
1. llamando a funcionLenta
at Object.<anonymous> (hola.spec.ts:12:11)
console.log
3. chequeando valor devuelto
at Object.<anonymous> (hola.spec.ts:18:11)
FAIL ./hola.spec.ts (59.798 s)
fallando el test asinc
✕ debe esperar el callback para completar (351 ms)
● fallando el test asinc › debe esperar el callback para completar
expect(received).toBe(expected) // Object.is equality
Expected: "completado"
Received: undefined
17 | });
18 | console.log("3. chequeando valor devuelto");
> 19 | expect(devuelto).toBe("completado");
| ^
20 | });
21 | });
22 |
at Object.<anonymous> (hola.spec.ts:19:20)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 total
Snapshots: 0 total
Time: 61.577 s
Ran all test suites.
● Cannot log after tests are done. Did you forget to wait for something async in your test?
Attempted to log "2. llamada completa".
13 | let devuelto !: string;
14 | mockAsync.funcionLenta((v: string) => {
> 15 | console.log("2. llamada completa");
| ^
16 | devuelto = v;
17 | });
18 | console.log("3. chequeando valor devuelto");
at console.log (node_modules/@jest/console/build/CustomConsole.js:141:10)
at hola.spec.ts:15:12
at Timeout._onTimeout (hola.spec.ts:4:4)
$
Demasiada informacion, no? En este caso fallo porque si observan paso del paso 1 al 3 pero nunca recibio el resultado del paso 2. Tambien nos informa que se esperaba un «completado» y se recibio un «undefined». Al final tambien tenemos una notificacion de una falla de log en el caso de la llamada a funcionLenta. Esto es debido a la demora que generamos y que el expect y toBe lo hacen de forma sincronica, es decir instantaneo, y esto no ocurre asi. Pero existe una solucion para esto y para ello debemos ir al describe y lo modificaremos de la siguiente manera:
describe("test asincronico con done", () => {
let devuelto!: string;
beforeEach((done: jest.DoneCallback) => {
let mockAsync = new MockAsync();
console.log("1. llamando a funcionLenta");
mockAsync.funcionLenta((v: string) => {
console.log("2. llamada completa");
devuelto = v;
done();
});
});
it("debe devolver el valor despues de 1 seg.", ()) => {
console.log("3. chequeando valor devuelto");
expect(devuelto).toBe("completado");
});
});
En este caso redefinimos un par de cosas. La primera es que declaramos a la variable que recibe el estado de completo del metodo de la clase. Luego utilizamos un beforeEach para configurar el test antes de ser ejecutado, sobre esto hablamos en este post, y aqui usaremos una funcion para este tema. Al beforeEach le aplicaremos una funcion y esta tiene un argumento de tipo jest.DoneCallback, la cual sera la que esperara por la conclusion de la demora, y en el bloque lo primero que haremos sera definir un objeto de la clase. Luego hacemos el llamado al metodo de la clase de la misma forma pero despues de establecido el valor llamaremos a la funcion done, en realidad es a la funcion DoneCallback.
Luego tenemos el test donde mostraremos el mensaje del valor devuelto y chequearemos si el valor en devuelto es el pasado en toBe. Probemos y veamos como es su salida:
$ npm test
> testing@1.0.0 test
> jest
console.log
1. llamando a funcionLenta
at Object.<anonymous> (hola.spec.ts:13:11)
console.log
2. llamada completa
at hola.spec.ts:15:12
console.log
3. chequeando valor devuelto
at Object.<anonymous> (hola.spec.ts:21:11)
PASS ./hola.spec.ts (59.332 s)
test asincronico con done
✓ debe devolver el valor despues de 1 seg. (1645 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 60.766 s
Ran all test suites.
$
En esta ocasion no solo paso el test, si miran la salida veran que tenemos los tres pasos, motivo por el cual pasa el test, y todo gracias al done que utilizamos. Este es el caso para cuando tenemos una demora pero que sucede cuando es una promesa? Para ese caso debemos usar al async y await de la misma manera que fuera un codigo normal. Tomemos el archivo que trabajamos y cambiemos el codigo de la siguiente manera:
class Prometido {
promesaDemorada(): Promise<string> {
return new Promise<string>(
(resuelto: (cdn: string) => void,
rechazado: (cdn: string) => void) => {
setTimeout(() => {
console.log("2. retorno exitoso");
resuelto("exitoso");
}, 1000);
}
)
}
}
describe("test asincronico", () => {
it("debe esperar 1 seg. para resolver la promesa",
async () => {
let prometido = new Prometido();
console.log("1. llamando promesaDemorada");
let devuelto = await prometido.promesaDemorada();
console.log("3. despues del await");
expect(devuelto).toEqual("exitoso");
})
});
La clase tendra un solo metodo, el cual devolvera una promesa. Esta tiene una funcion donde tiene dos argumentos, el primero sera para cuando se realizo la promesa y el segundo sera para cuando se rechazo o fallo. y despues en el bloque tendremos un timeout donde mostraremos en consola el mensaje de retorno exitoso y mediantte resuelto devolveremos ese mensaje. La demora es de tan solo 1000 ms o 1 seg., tal como hicimos en el ejemplo anterior.
Luego tenemos el test donde primero usaremos a async para indicar que esa funcion es asincronica. Primero crearemos un objeto de la clase, luego mostramos en consola un mensaje del llamado al metodo. Lo siguiente es crear una variable que almacenara lo devuelto por el metodo promesaDemorada y le aplicamos un await, esto hara que espere hasta que se haya recibido el valor. Para despues mostrar un mensaje indicando que esto es despues del await y por ultimo usamos a expect para comparar el valor en devuelto con respecto al valor informado en toEqual. Con esto podemos pasar a probar nuestro test, veamos como es su salida:
$ npm test
> testing@1.0.0 test
> jest
console.log
1. llamando promesaDemorada
at hola.spec.ts:19:13
console.log
2. retorno exitoso
at Timeout._onTimeout (hola.spec.ts:7:14)
console.log
3. despues del await
at hola.spec.ts:21:13
PASS ./hola.spec.ts (56.593 s)
test asincronico
✓ debe esperar 1 seg. para resolver la promesa (1492 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 57.697 s, estimated 60 s
Ran all test suites.
$
Como pueden ver funciono el test porque gracias al async y el await, al haber una promesa en el medio nos permite poder utilizarlas y el codigo esperara hasta que esa promesa sea completada, ya sea para resuelta como por rechazada. Tambien podemos ver que el codigo se acomodo de una manera mas organica sin tener que agregar nuevos elementos como vimos en el ejemplo anterior. Antes de finalizar les dejo un link con los archivos utilizados y modificados en este post:
Nota:
En el archivo encontraran el ultimo codigo que trabajamos.
En resumen, hoy hemos visto como son los test asincronicos, para que nos pueden ser utiles, un ejemplo donde los necesitamos, una forma de manejarlos mediante DoneCallback, luego mediante async y await con una promesa. Espero les haya resultado de utilidad sigueme en tumblr, Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.


Donación
Es para mantenimento del sitio, gracias!
$1.50
