Tengo un controlador simple para agregar nuevos usuarios. Después de la resolución exitosa (usuario agregado), el controlador envía una respuesta 202. Como puede ver, la función usa then/catch y no usa async/await.
const addUserController = function (req, res, next) { Users.addOne(req.userid, req.body.email) .then(() => { res.status(202).send(); }) .catch((err) => { console.log(err); res.status(500).json({ message: "Internal server error." }); }); };Cuando pruebo esta función en Jest con the, la función se ejecuta inmediatamente, sin ir a la parte then(), lo que da como resultado un código 200 erróneo, en lugar de 202, por lo que falla la siguiente prueba:
it("Should add a user", () => { let req, res, next, pool; pool = new Pool(); req = httpsMocks.createRequest(); res = httpsMocks.createResponse(); res.next = null; req.userid = 1; req.body = { id: 2 } pool.query.mockResolvedValue({rows:[], rowCount: 1}); apiController.addUserController(req, res, next); expect(res.statusCode).toBe(202); expect(pool.query).toBeCalledTimes(1); });Sin embargo, cuando lo hago así:
it("Should add a user", async () => { let req, res, next, pool; pool = new Pool(); req = httpsMocks.createRequest(); res = httpsMocks.createResponse(); res.next = null; req.userid = 1; req.body = { id: 2 } pool.query.mockResolvedValue({rows:[], rowCount: 1}); await apiController.addUserController(req, res, next); expect(res.statusCode).toBe(202); expect(pool.query).toBeCalledTimes(1); });es decir, agrego async/await, funciona bien: el código de estado de respuesta es 202, lo que significa que se esperaba la función y se pasa la prueba. ¿Pero por qué? Cuando paso el cursor sobre el código VS 'esperar' recién agregado, sugiere que
'await' no tiene ningún efecto sobre el tipo de esta expresión.
Bueno, tiene sentido, no debería tener ningún efecto, ya que la función probada no es asíncrona, por lo que no debería funcionar, pero bueno, funciona, solo cuando agrego async/await a la función Jest, funciona bien.
¿Podría alguien explicarme esto?
Agrego async/await, funciona bien: el código de estado de respuesta es 202, lo que significa que se esperaba la función y se pasa la prueba. ¿Pero por qué?
No, como concluyó a partir del valor de retorno que falta, la función no se espera. Su código es equivalente a
apiController.addUserController(req, res, next); await undefined; Ahora bien, ¿por qué sigue marcando la diferencia? Porque con await , la prueba espera un poquito antes de ejecutar las llamadas a expect() , y ese poquito es suficiente para que su grupo simulado devuelva un valor y se ejecute el primer controlador .then() .
Sin embargo, ahora básicamente introdujiste una condición de carrera. Tener una cadena de promesa más larga en addUserController haría que la prueba fallara. Tal vez incluso una prueba para el estado 500 que se está creando en .then().catch() ya no funcione.
Esto es muy frágil, no escribas una prueba como esa. Una solución sería simplemente return la cadena de promesas de addUserController y await , siempre que no confunda a otras personas que llaman, es la solución simple. Otra solución es esperar a que se envíe la respuesta simulada. Si leo bien los documentos de node-mocks-http , algo como esto debería funcionar:
it("Should add a user", async () => { const { once, EventEmitter } = require('events'); const pool = new Pool(); const req = httpsMocks.createRequest(); const res = httpsMocks.createResponse({ eventEmitter: EventEmitter }); res.next = null; req.userid = 1; req.body = { id: 2 } pool.query.mockResolvedValue({rows:[], rowCount: 1}); const responseEndPromise = once(res, 'end'); apiController.addUserController(req, res, next); await responseEndPromise; expect(res.statusCode).toBe(202); expect(pool.query).toBeCalledTimes(1); });