Digamos que tengo la siguiente función que verifica periódicamente una condición hasta que se cumple.
let loopWithSleep = (callback) => { if (conds.every(cond => { return cond; })) { console.log("wooho"); callback(); } else { console.log("waiting..."); setTimeout(loopWithSleep, 500, callback); } };Y supongamos que tengo un conjunto de pruebas que se parece a esto:
describe('Callback', () => { it('should NOT use the provided callback', (done) => { var conds = [true, false]; loopWithSleep(done); }); }); ¿Cómo puedo probar si una función de devolución de llamada (en el ejemplo anterior llamada done ) no ha sido llamada y aún así pasa la prueba?
El ejemplo anterior da como resultado un error: Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.
Estaba tratando de envolver la prueba en chai.assert.throws( ... ); pero sin éxito.
En primer lugar, el diseño de la función podría mejorar. Se basa en la variable global cond , que es frágil; si cond cambia de nombre en otra parte del código, la función se interrumpe. Todos los datos deben pasarse como parámetros. Esto ayuda a aislar componentes y mitigar errores y facilita las pruebas.
Sugeriría una reescritura inicial como:
const callWhenConditionsMet = (callback, conds, ms=500) => { if (conds.every(Boolean)) { callback(); } else { setTimeout(callWhenConditionsMet, ms, callback, conds, ms); } };Esto todavía parece subóptimo debido a la falta de promesas o argumentos, sin mencionar el uso de encuestas cuando un enfoque basado en eventos probablemente sea más apropiado (un posible problema XY ), pero asumiré que es lo suficientemente bueno para su caso de uso.
En cuanto a la prueba, puede estar confundiendo done() con un simulacro/espía. done() no es apropiado para aserciones; solo está ahí para indicarle a Mocha que la prueba finalizó cuando se trata de devoluciones de llamada asincrónicas. Es esencialmente un atajo a la promisificación. Tener que usar done en exceso puede indicar un diseño deficiente en su biblioteca o conjunto de pruebas (o ambos). Prefiere las promesas.
En lugar de done , simule la devolución de llamada con su biblioteca favorita, como Sinon, pase el simulacro a la función que está probando, avance el tiempo según sea necesario y luego afirme que se llamó (o no) al simulacro.
Además, utilice temporizadores falsos para evitar que sus pruebas tomen mucho tiempo y para garantizar la confiabilidad. Si está utilizando temporizadores falsos, ¡no es necesario que lo done en absoluto! (otro problema XY)
const {assert} = require("chai"); const sinon = require("sinon"); const callWhenConditionsMet = (callback, conds, ms=500) => { if (conds.every(Boolean)) { callback(); } else { setTimeout(callWhenConditionsMet, ms, callback, conds, ms); } }; describe("callWhenConditionsMet", () => { let clock; let spy; beforeEach(() => { clock = sinon.useFakeTimers({toFake: ["setTimeout"]}); spy = sinon.spy(); }); afterEach(() => { clock.restore(); spy = null; }); it("should invoke the callback right away when all conditions are true", () => { callWhenConditionsMet(spy, [true, true]); assert.isTrue(spy.called); }); it("should not invoke the callback when a condition is false", () => { callWhenConditionsMet(spy, [true, false]); clock.tick(2000); assert.isFalse(spy.called); }); it("should not initially call when false, but then call later when true", () => { const conds = [false, true]; callWhenConditionsMet(spy, conds, 1000); clock.tick(5000); assert.isFalse(spy.called); conds[0] = true; clock.tick(2000); assert.isTrue(spy.called); }); });Si bien la respuesta proporcionada por @ggorlen es ciertamente útil, creo que está demasiado diseñada.
A continuación se muestra mi respuesta inspirada en su comentario a la pregunta. Utiliza Mocha y chai con el complemento chai-spies .
describe("callWhenConditionsMet", () => { beforeEach(() => { conds = [true, false]; }); it('should eventually use the callback function', async function () { const mock = chai.spy(); loopWithSleep(mock); await new Promise(function(resolve) { setTimeout( function() {resolve(); }, 500);}); chai.expect( mock ).not.to.have.been.called(); conds[1]=true; await new Promise(function(resolve) { setTimeout( function() {resolve(); }, 500);}); chai.expect( mock ).to.have.been.called(); }); });