Testing a Promise using setTimeout with Jest - javascript

I'm trying to understand Jest's asynchronous testing.
My module has a function which accepts a boolean and returns a Promise of a value. The executer function calls setTimeout, and in the timed out callback the promise resolves or rejects depending on the boolean initially supplied. The code looks like this:
const withPromises = (passes) => new Promise((resolve, reject) => {
const act = () => {
console.log(`in the timout callback, passed ${passes}`)
if(passes) resolve('something')
else reject(new Error('nothing'))
}
console.log('in the promise definition')
setTimeout(act, 50)
})
export default { withPromises }
I'd like to test this using Jest. I guess that I need to use the mock timers Jest provides, so my test script looks a bit like this:
import { withPromises } from './request_something'
jest.useFakeTimers()
describe('using a promise and mock timers', () => {
afterAll(() => {
jest.runAllTimers()
})
test('gets a value, if conditions favor', () => {
expect.assertions(1)
return withPromises(true)
.then(resolved => {
expect(resolved).toBe('something')
})
})
})
I get the following error/failed test, whether or not I call jest.runAllTimers()
Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
Can you explain where I'm going wrong and what I might do to get a passing test that the promise resolves as expected?

The call to jest.useFakeTimers() mocks every timer function with one that you must control. Instead of the timer running automatically, you would advance it manually. The jest.runTimersToTime(msToRun) function would advance it by msToRun milliseconds. It's very common that you want to fast-forward until every timer has elapsed and it would be cumbersome to calculate the time it takes for all the timers to finish, so Jest provides jest.runAllTimers(), which pretends that enough time has passed.
The problem in your test is that you never call jest.runAllTimers() in the test, but you call it in the afterAll hook, which is called after the tests have finished. During your test the timer remains at zero so your callback is never actually called and Jest aborts it after a predefined interval (default: 5s) to prevent being stuck with a potentially endless test. Only after the test has timed out, you call jest.runAllTimers(), at which point it doesn't do anything, since all tests have already finished.
What you need to do is launch the promise and then advance the timer.
describe('using a promise and mock timers', () => {
test('gets a value, if conditions favor', () => {
expect.assertions(1)
// Keep a reference to the pending promise.
const pendingPromise = withPromises(true)
.then(resolved => {
expect(resolved).toBe('something')
})
// Activate the timer (pretend the specified time has elapsed).
jest.runAllTimers()
// Return the promise, so Jest waits for its completion and fails the
// test when the promise is rejected.
return pendingPromise
})
})

Related

How come the data runs from my promise without calling it

So I'm currently learning about promises in JavaScript, and I tried making my first promise and realized that it ran the setTimeout() even if I didn't call the promise to begin with, just by defining it, it ran by itself. This being postLogin.
const postLogin = new Promise((resolve, reject) => {
setTimeout(() => {
console.log("Token: 1234")
}, 2000);
});
^ This logs out "Token: 1234" without calling postLogin.then()
Where as this doesn't run until I call postLogin1.then(token => {console.log(token)});
const postLogin1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Token1: 1234");
}, 2000);
});
postLogin1.then(token => {console.log(token)});
Why is it that if I don't add "resolve" it runs the code without invoking it?
Promise constructors call the function you pass to them immediately. This is the case in both your examples.
If you have console.log inside that function, then it will call console.log. If you don't, then it won't. That's the key difference between the examples.
The then method just adds an event handler that will be called when the promise is resolved (or immediately if it has already been resolved).
If you have console.log in the then handler, then it will be called then the function resolves.
If you don't ever call resolve then it will never resolve. The function you pass to it is still called.

How do I test a recursive, asynchronous JavaScript function using fake timers?

I have a basic recursive function, which works correctly when executed. I would like to use Sinon's fake timers to test the function at each stage of execution.
However, it seems that the fake timers are only applying to the first call of the recursive function.
I'm curious if anyone can help figure out how to get the fake timers working all the way through.
Example:
function wait(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
async function ensureCount(count, { attempt, delay }) {
attempt = attempt || 0
console.log('Attempt', attempt)
if (attempt === count) {
return
}
await wait(delay)
await ensureCount(count, { attempt: attempt + 1, delay })
}
Tested like so (with the help of this article):
it('retries after a given delay', function() {
const clock = sinon.useFakeTimers()
const promise = ensureCount(2, { delay: 200 })
clock.tick(200)
// Make some assertions.
clock.tick(200)
// Make some assertions.
clock.tick(200)
// Make some assertions.
return promise
})
The expected console output (which is what happens without fake timers):
Attempt 0
Attempt 1
Attempt 2
✔ retries after a given delay (405ms)
The actual console output (which happens with fake timers):
Attempt 0
Attempt 1
Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.
The Question:
Is there a way to get the fake timers to apply to every step of the recursive call, rather than just the first one?
Turns out Sinon provides a .tickAsync() function for this that I hadn't seen. (Thanks to this comment).
This code resolves the issue:
it('retries after a given delay', async () => {
const clock = sinon.useFakeTimers()
ensureCount(2, { delay: 200 })
await clock.tickAsync(200)
// Make some assertions.
await clock.tickAsync(200)
// Make some assertions.
await clock.tickAsync(200)
// Make some assertions.
clock.restore()
})

How can I run watch changes in asynchronous way NodeJS Mongoose

For the past 2 days I have been struggling on this thing. I want to watch for changes on particular collection. All of the logic for these changes is in asynchronous way. I tried to make the EventEmitter callback function to be asynchronous but still works in synchronous manner.
Pseudo code example:
const someLogicFunction1 = async () => {
// some logic here
}
const someLogicFunction2 = async () => {
// some logic here
}
const collectionEventEmitter = Collection.watch();
collectionEventEmitter.on('change', async (change) => {
await someLogicFunction1();
await someLogicFunction2(); // has to wait for first function to finish
});
As I explained above I tried this approach but still runs synchronously. The second function usually has to wait for the first function to finish its task in order for the whole code to be properly working.
EDIT:
From what I have found it seems that the callback function firstly gets all this "change" parameter event objects and then executes the code. What I need actually is to execute the code for every "change" event parameter - not for all of them at once.
One solution is to make someLogicFunction1() returning a promise and calling someLogicFunction2(); in then of first function, like this:
await someLogicFunction1()
.then(function(){
someLogicFunction2();
});
You just need to modify someLogicFunction1(); like this:
someLogicFunction1( )
{
return new Promise( async function( resolve, reject ) {
// Do your stuff here
}); // eo promise
}
Don't forget to resolve() in someLogicFunction1 function.
resolve();
You can also reject() in someLogicFunction1 and catch the error when you call this function.
reject();
You can pass an argument to resolve and reject and get it in your call.

Unit testing NodeJS Promise inside a function

Im trying to unit test a function that calls a promise...
Using Mocha, Sinon. I have a functional block like this:
myfile.js:
let OuterDependecy = require('mydep');
function TestFunction(callback) {
OuterDependency.PromiseFunction().then(response => {
//some logic here
}).catch(err => {callback(err)});
inside my test i have used proxyquire to mock the outerdependecy
testfile.js
let proxyquire = require('proxyquire');
let OuterDepStub = {};
let testingFunc = proxyquire('myfile.js', {'mydep': OuterDepStub});
... then inside my testing block
let stubCallback = function() {
console.log('Stub dubadub dub'); //note...i can use sinon.spy here instead
};
beforeEach(()=>{
OuterDependency.PromiseFunction = function(arg) {
return new Promise((resolve, reject)=>{
reject('BAD');
});
};
spy = sinon.spy(stubCallback);
});
my actual test now calls the main "testfunction"
it('Catches Errors, and calls back using error', done => {
TestFunction(stubCallback);
expect(spy).to.have.been.called;
done();
});
I see the stub being called (the console log, hence why i didnt want to use sinon.spy) but the spy is saying its not called. and unit test fails.
I believe this is probably due to a race condition of sorts where the promise is resolving after my test is run... is there anyway to delay the test until my promise is resolve.
i know in in angularjs promise testing, there was a way to "tick" the promise so it resolves when you want to. possible in nodejs?
is there anyway to delay the test until my promise is resolve.
As far as I understand your issue, yes, you should only call done() after the promise is settled. In order to do that,you need two things:
1- Enforce TestFunction to return a Promise, so you can wait until it resolves:
function TestFunction(callback) {
return OuterDependency.PromiseFunction().then(response => {
//some logic here
}).catch(err => { callback(err) });
}
2- Wait to that promise to settle, then call done.
it('Catches Errors, and calls back using error', done => {
TestFunction(stubCallback).then(() => {
expect(spy).to.have.been.called;
done();
})
});
now, our then block won't run until the catch block within TestFunction, so if the test works as expected (i.e. the catch block fires and the callback gets fired), the expectation and the done calls will always fire after the callback gets called.
I see the stub being called (the console log, hence why i didnt want to use sinon.spy) but the spy is saying its not called. and unit test fails.
That's because your expectation runs right after the TestFunction calls, without waiting for it to settle. However, it will get called lately, thus the console.log appears in the next spec.

How can I test that a Promise is never fulfilled?

I'm testing a library I wrote to throttle execution of a function.
The API is throttler.do(fn) and it returns a promise of fn's return value (resolved at whichever point the throttler decides it's ok to run it).
I'm using lolex to fake Date() and setTimeout so if I set the throttler to allow two actions per minute and do
throttler.do(() => {});
throttler.do(() => {});
throttler.do(() => {}).should.eventually.equal(5);
this fails as expected (it times out because it's waiting on the last promise forever since I never call lolex.tick).
Is there a way I can turn this into a passing test, something like
throttler.do(() => {}).should.never.be.fulfilled;
I can't do
setTimeout(() => done(), 1500)
because the setTimeout method is faked by lolex.
You can mock the Promise to be synchronous and then check the Promise state. Here is a library you can use promise-sync

Categories