Tengo un conjunto de pruebas de integración para el código de cola de mensajes (sobre un RabbitMQ real). Las pruebas verifican que el caso de uso correcto se activa cuando llega un mensaje específico.
it("should trigger the use case", () => { const stub = sinon.stub(app, 'useCaseToTrigger').resolves(); await publishMessage({ id: "SOME_ID" }) // FAIL - Message isn't processed yet. stub.should.have.been.calledWith(match({id: "SOME_ID" })) }) esto no funciona La instrucción await espera a que se publique el mensaje, pero no indica cuándo se procesa el mensaje.
Puedo agregar un retraso, pero eso rápidamente se convierte en una gran pérdida de tiempo (si hay varias pruebas).
it("should trigger the use case", () => { const stub = sinon.stub(app, 'useCaseToTrigger').resolves(); await publishMessage({ id: "SOME_ID" }) await new Promise(r => setTimeout(r, 100)); // PASS - the message has been processed before the verification stub.should.have.been.calledWith(match({id: "SOME_ID" })) })Pero si hago que la función stubbed resuelva una promesa cuando se llama, puedo hacer que mi prueba funcione sin demoras indebidas.
it("should trigger the use case", () => { const promise = new Promise(r => { sinon.stub(app, "useCaseToTrigger").callsFake(() => { setImmediate(() => { r(); }) // Push to the end of the event loop return Promise.resolve(); }) }) await publishMessage({ id: "SOME_ID" }) await promise; // PASS - Message processed before continuing test app.useCaseToTrigger.should.have.been.calledWith(match({id: "SOME_ID" })) })Esto se vuelve un poco engorroso de manejar (y probablemente tampoco sea el más fácil de leer). Pero también da advertencias de promesas flotantes de eslint. Entonces, para evitar esas advertencias, necesito reescribirlo a:
it("should trigger the use case", () => { await Promise.all([ new Promise(r => { sinon.stub(app, "useCaseToTrigger").callsFake(() => { setImmediate(() => { r(); }) // Push to the end of the event loop return Promise.resolve(); }) }), publishMessage({ id: "SOME_ID" }) ]); // PASS - Message processed before continuing test app.useCaseToTrigger.should.have.been.calledWith(match({id: "SOME_ID" })) })¿Hay alguna manera de hacer esto más limpio?
Creo que puede usar una promesa vacía para esperar, por lo que la lógica de la aplicación finalmente resuelve esa promesa cuando se llama. Sinon proporciona una promesa falsa que hace que esto sea un poco más fácil que crear una new Promise con una referencia externa a la función de resolve .
const p = sinon.promise() const stub = sinon.stub(app, 'useCaseToTrigger').callsFake(p.resolve) await publishMessage({ id: "SOME_ID" }) await p sinon.assert.calledWith(stub, { id: "SOME_ID" }) Parece que sinon usa Promise.resolve en el momento de la llamada al código auxiliar, por lo que no tiene una referencia a la eventual promesa en la creación del código auxiliar; de lo contrario, un tipo de referencia stub.promise podría ser útil aquí.