¿Cómo probar una función llamada recursivamente en NodeJS?

Tengo una función recurrente escrita en ES6 / 7 que es transstackda por babel. Tengo una función recurrente creada que verifica si hay un documento de usuario, usando mongoose.

// Keep checking if there is a user, if there is let execution continue export async function checkIfUserExists(){ let user = await User.findOneAsync({}); // if there is no user delay one minute and check again if(user === null){ await delay(1000 * 60 * 1) return checkIfUserExists() } else { // otherwise, if there a user, let the execution move on return true } } 

Si no hay un usuario, estoy utilizando la biblioteca de retardo para retrasar la ejecución durante un minuto en el que se llama a la función de forma recursiva.

Esto permite detener la ejecución de la función general hasta que se encuentre un usuario:

 async function overallFunction(){ await checkIfUserExists() // more logic } 

La twig else es muy fácil de generar pruebas para. ¿Cómo creo una prueba para la twig if que verifica que la recursión está funcionando correctamente?

En este momento, he reemplazado el método de retardo con proxyquire durante la prueba, sustituyéndolo por una función de retardo personalizada que solo devuelve un valor. En ese momento puedo cambiar el código para que se vea así:

 // Keep checking if there is a user, if there is let execution continue export async function checkIfUserExists(){ let user = await User.findOneAsync({}); // if there is no user delay one minute and check again if(user === null){ let testing = await delay(1000 * 60 * 1) if (testing) return false return checkIfUserExists() } else { // otherwise, if there a user, let the execution move on return } } 

El problema es que el código fuente se está cambiando para adaptarse a la prueba. ¿Hay una solución mejor y más limpia?

No estoy seguro de por qué quería usar una solución recursiva en lugar de una solución iterativa, pero podría ser más fácil escribirla de forma iterativa, por la única razón de que no haga volar la stack:

  do{ let user = await User.findOneAsync({}); // if there is no user delay one minute and check again if(user === null){ await delay(1000 * 60 * 1); } else{ return true; } }while (!user); 

No he probado o ejecutado esto con un intérprete, pero entiendes la idea.

Luego, en su modo de prueba, simplemente proporcione un usuario de prueba. Ya que probablemente necesitará escribir pruebas que usen una referencia al usuario de todos modos.

Hay varias bibliotecas que se pueden usar para probar eventos relacionados con el tiempo. Por lo que sé, la solución más común es Lolex – https://github.com/sinonjs/lolex , parte anterior del proyecto Sinon. El problema con Lolex es que reenvía los temporizadores de manera sincrónica, ignorando eventos como promesas nativas de nodo o process.nextTick . Tenga cuidado con las bibliotecas externas, por ejemplo, bluebird almacena en caché el setImmediate inicial, por lo que necesita manejarlo de alguna manera manualmente.

Una opción diferente es Zurvan – https://github.com/Lewerow/zurvan (descargo de responsabilidad: lo escribí). Es un poco más difícil de abordar que Lolex, ya que se usa mucho con promesas, pero se comporta correctamente en presencia de tareas de microcoqueos ( process.nextTick , nativo Promise ) y tiene una opción de compatibilidad integrada para bluebird.

Ambas bibliotecas le permiten caducar los eventos relacionados con el tiempo en la longitud de los arbitrajes y anular también las instancias de Date (zurvan anula process.uptime y process.hrtime también). Ninguno de ellos es seguro de usar si realiza IO asíncrono real en las pruebas.

He escrito un ejemplo de cómo puedes probar tu función llamada recursivamente aquí:

https://jsfiddle.net/Fresh/qppprz20/

Esta prueba hace uso de la biblioteca de pruebas javascript de Sinon . Puede configurar el comportamiento del apéndice en la enésima llamada, por lo tanto, puede simular cuando no se devuelve ningún usuario y, posteriormente, cuando se devuelve un usuario, por ejemplo

 // Stub the method behaviour using Sinon javascript framework var user = new User(); var userStub = sinon.stub(user, 'findOneAsync'); userStub.onFirstCall().returns(null); userStub.onSecondCall().returns({}); 

Por lo tanto, onFirstCall simula la primera llamada y onSecondCall la llamada recursiva.

Tenga en cuenta que en el ejemplo completo he simplificado checkIfUserExists, pero la misma premisa de prueba se aplicará a su método completo. También tenga en cuenta que, además, tendría que aplazar el método de retraso.