Testing asynchronus delay function in Karma - javascript

I have a bit of an unusual problem that I haven't found any solution for and I've been trying for quite some time. I have created a function called delay that basically creates a new Promise which resolves itself after a given amount of time. The purpose of this function is to be able to cause a delay in a chain of promises. It looks a bit like this:
const delay = ms => new Promise((resolve, reject) => {
setTimeout(resolve, ms);
});
let test = '';
const func = () => {
delay(1000).then(() => {
test = 'kek';
});
};
describe('unit tests', () => {
it('test function func', () => {
// Act
func();
// Assert
expect(test).toEqual('kek');
});
});
The problem is testing, the unit test I have provided in this example will fail because it's only after the 1000 ms delay that the variable test is set to kek.
I have tried the usual solutions like having a setTimeout inside the unit test, and also tried with jasmine.clock().tick(1001) before asserting but I can't get it to work.
Any ideas?

I actually proposed to do this (at lolex, the timers library) and am now convinced that it's a good thing that this is impossible to override the scheduler for that reason. That's because it can change the ordering of calls which can be very problematic.
Promises always resolve asynchronously, you'll have to make your test async and return the promise.
Here is how you'd write that test with an async function:
describe('unit tests', () => {
it('test function func', async () => {
// Act
let promise = func(); // wait for it to be ready
clock.tick(1001);
await promise; // wait for the promise to complete.
// Assert
expect(test).toEqual('kek');
});
});
This way you won't wait 1000 ms but the test will still work.

This works for me,
import {fakeAsync, tick } from '#angular/core/testing';
describe('When ngOnInit is invoked', () => {
it('should wait and redirect to next page', fakeAsync( () => {
component.ngOnInit(); // contains delay inside
tick(2000); // time to wait
expect(routerService.navigate).toHaveBeenCalledWith('nextStep');
}));
});

Related

jest.mock function not being used after a mockRestore is called on a different mocked function

I am trying to mock the AWS SDK. I have a function, ingest that does some logic (including some calls to the AWS SDK sqs.deleteMessage function) and then calls ingestAndSave (which also makes some calls to the sqs.deleteMessage function).
When testing the ingest function, I want to mock my ingestAndSave function so I can have those tests focus purely on the ingest function logic.
Then I want to test the ingestAndSave function logic in its own describe block - and test the real thing, not the mocked version.
When testing either ingest or ingestAndSave functions I need to use my mocked sqs.deleteMessage
My most recent attempt includes following the path here: https://www.lucasamos.dev/articles/jestmocking. My test suite looks like this:
const ingest = require('./ingest')
const TEST_CONSTANTS = require('../../../tests/constants')
const mockdeleteMessage = jest.fn().mockImplementation( ()=> {
console.log("MOCK DELETE MESSAGE FUNCTION");
return {}
} )
jest.mock('aws-sdk', () => {
return {
config: {
update() {
return {}
}
},
SQS: jest.fn(() => {
return {
deleteMessage: jest.fn(( { params } )=> {
return {
promise: () => mockdeleteMessage()
}
}),
}
})
}
});
describe("FUNCTION: ingest", () => {
let ingestAndSaveFunctionSpy
beforeEach(()=> {
ingestAndSaveFunctionSpy = jest.spyOn(ingest, 'ingestAndSave').mockReturnValue(
Promise.resolve() )
})
test("ingestAndSave will be called two times when event has two Records", async () => {
await ingest.ingest(TEST_CONSTANTS.sqsIngestEventMessageWithTwoRecords)
expect(ingestAndSaveFunctionSpy).toHaveBeenCalledTimes(2)
})
afterEach(() => {
jest.resetAllMocks()
})
afterAll(() => {
jest.restoreAllMocks()
})
})
describe("FUNCTION: ingestAndSave", () => {
let validParams = {camera: TEST_CONSTANTS.validCameraObject, sourceQueueURL:"httpexamplecom", receiptHandle: "1234abcd"}
test("Will call deleteMessage once when given valid paramters", async () => {
await ingest.ingestAndSave(validParams)
expect(mockdeleteMessage.mock.calls.length).toBe(1)
})
/** snip other tests */
})
Both of the above functions run through code that looks just like this:
let deleteResp;
try {
deleteResp = await sqs.deleteMessage({ QueueUrl: sourceQueueURL, ReceiptHandle: receiptHandle}).promise()
} catch (err) {
console.error('Task: Deleting message from the ingest queue: ', err, 'deleteResp: ', deleteResp, 'receiptHandle: ', receiptHandle)
}
The mocked sqs.deleteMessage is used for the first describe block (i.e. I see the console.log for it) and the test passes.
However, the mocked sqs.deleteMessage function is not used for the second describe block (i.e. I do not see the console.log message indicating the mocked function ran, and, in fact, I get a 5000ms timeout, indicating the real sqs.deleteMessage was called (with invalid authorization, the deleteMessage command takes >5 seconds).
I thought the jest.restoreAllMocks() in the afterAll block of the first describe is restoring my mock. So I go with explicitly restoring the ingestAndSaveFunctionSpy mock with ingestAndSaveFunctionSpy.mockRestore() instead. Unfortunately, this results in the same behavior: the mocked AWS SDK is used in the first describe block, but it has been restored by the ingestAndSaveFunctionSpy.mockRestore() call.
Just to test, I remove the afterAll in the first describe entirely. This results in the second test calling the mocked implementation of ingestAndSave and thus the test failing.
Declare the jest.mock... within each describe block, but this isn't allowed due to jest.mock calls getting hoisted to the top of the file.
How can I mock a module using jest.mock(... and have it persist between describe blocks while allowing mockRestore() calls to other mocked functions?
How to change mock implementation on a per single test basis? has me looking at mockImplemention but I'm struggling to see how I'd implement it.
See related question attempting to tackle this from a different angle: How to have jest.mock persist between tests?
If you only want to reset the one mock, then don't reset all of the mocks. Just reset the spy:
describe("FUNCTION: ingest", () => {
let ingestAndSaveFunctionSpy
beforeEach(()=> {
ingestAndSaveFunctionSpy = jest.spyOn(ingest, 'ingestAndSave').mockReturnValue(
Promise.resolve() )
})
// ...
afterEach(() => {
ingestAndSaveFunctionSpy.mockRestore()
})
})
In the outer scope, I would also advise using a:
afterEach(() => {
jest.clearAllMocks()
})
This will reset the recordings of the mock calls to your aws-sdk mock without actually changing any of the mocked behavior. Otherwise, you might be verifying behavior in one test that actually happened during the execution of another.
Without seeing the code, it's hard to offer more specific advice, but this style of test is a code smell, IMO. Any time you have to spy on your own unit in a unit test, it's usually a sign of poor organization. Try to arrange it so that discreet units of work are discreet functions. As an abstract example, instead of chaining functions together like:
const a = (arg) => {
const stuff = /* do some stuff with arg */
b(stuff);
};
const b = (arg) => {
const stuff = /* do some stuff with arg */
c(stuff);
}
const c = (arg) => {
/* do some stuff with arg */
}
Instead make them discreet, testable units that are joined together in the external function:
const abc = (arg) => {
const firstStuff = a(arg);
const secondStuff = b(firstStuff);
c(secondStuff);
}
const a = (arg) => {
return /* do some stuff with arg */
}
const b = (arg) => {
return /* do some stuff with arg */
}
const c = (arg) => {
/* do some stuff with arg */
}
Now you can write tests for a, b, and c independently without having to mock your own internals.
I solved this by wrapping all calls to aws-skd functions in my own functions and then mocking my wrapper functions.
So now I have a utility functions in utils.js that looks like this:
module.exports.sendMessage = async (params) => {
return await sqs.sendMessage(params).promise()
}
And in my real function code, I call it this way (instead of calling sqs.sendMessage directly - a super-easy change):
await utils.sendMessage(sendMessageParams).catch(err => throw (err))
And then during testing, I mock it like this - either in a beforeEach or directly before my test:
sendMessageSpy = jest.spyOn(utils, 'sendMessage').mockReturnValue(Promise.resolve({success:true}))
Later, rinse, and repeat for all aws-sdk functions I want to mock.

How to fire a function only after the first one has finished in (JavaScript)

I'm not looking for jQuery solutions please. I tried using the Promise method, and it wouldn't work either I'm trying the callback method but no luck. For right now, as a test I just want it to print something to the console. I'll do the actual code after.
I have a JSFIDDLE HERE for your coding pleasure
This is the callback method I am trying now as I think it's the simplest
const logoAnimation = () => {
this.logo.classList.add('fade-out-zoom-out');
this.blueCnt.classList.add('fade-out');
this.circWrapper.classList.add('dashed-border');
this.clickAbove.classList.add('text-fade-out');
this.welcome.classList.add('text-fade-in');
}
const rotateBorder = () => {
console.log('I run after animation')
}
const render = () => {
console.log(logoAnimation())
logoAnimation(() => {
rotateBorder();
})
}
I did try the Promise method... I'll take either one to be frank (An explanation of it would be ever better please)
function logoAnimation() {
return new Promise(function(resolve, reject) {
this.logo.classList.add('fade-out-zoom-out');
this.blueCnt.classList.add('fade-out');
this.circWrapper.classList.add('dashed-border');
this.clickAbove.classList.add('text-fade-out');
this.welcome.classList.add('text-fade-in');
})
}
const rotateBorder = () => {
console.log('I run after animation')
}
function render() {
logoAnimation().then(function () {
rotateBorder()
})
}
Then just an onclick call from somewhere
<img class="logo" id="logo" onclick="render()" src="/path/img"/>
Your problem is essentially the same in both versions.
In the first one the implementation of logoAnimation doesn't care about the callback you pass it - in fact it doesn't accept any arguments at all, so it will happily ignore anything passed to it. If you intend to pass it a callback, then you need to ensure that you call it ("call it back" - this is where the term "callback" comes from!) inside the function:
const logoAnimation = (callback) => {
this.logo.classList.add('fade-out-zoom-out');
this.blueCnt.classList.add('fade-out');
this.circWrapper.classList.add('dashed-border');
this.clickAbove.classList.add('text-fade-out');
this.welcome.classList.add('text-fade-in');
callback();
}
In the Promise version, you likewise never call resolve, which is the function passed in to the .then handler. So to get that version to work, you would need to change the implementation like this:
function logoAnimation() {
return new Promise(function(resolve, reject) {
this.logo.classList.add('fade-out-zoom-out');
this.blueCnt.classList.add('fade-out');
this.circWrapper.classList.add('dashed-border');
this.clickAbove.classList.add('text-fade-out');
this.welcome.classList.add('text-fade-in');
resolve();
})
}
I will note that, although in general I'm a big fan of using Promises for asynchronous code rather than old-style spaghetti callbacks, there's nothing asynchronous going on in your examples so I see no need for Promises - I'd just stick with the callback-based version, assuming you need callers of logoAnimation to specify what should happen next.
Unfortunately, css transitions are out of control of JavaScript. Your problem is not with the order of functions.
In order to achieve what you want, you should use setTimeout function and calculate those durations yourself.
You can use a "wait" function in order to mimic waiting:
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
let logo = document.getElementById('logo');
let blueCnt = document.getElementById('blueCnt');
let circWrapper = document.getElementById('circWrapper');
let clickAbove = document.getElementById('clickAbove');
let welcome = document.getElementById('welcome');
function logoAnimation() {
this.logo.classList.add('fade-out-zoom-out');
this.blueCnt.classList.add('fade-out');
this.circWrapper.classList.add('dashed-border');
this.clickAbove.classList.add('text-fade-out');
this.welcome.classList.add('text-fade-in');
}
const rotateBorder = () => {
console.log('I run after animation')
}
function render() {
logoAnimation()
wait(1000).then(rotateBorder)
}
Also pay attention that if you do not call the resolve function inside a Promise callback, it won't fulfill and your function call won't reach the chained .then
If you want to run 2 synchronous functions in sequence, you can just run them one at a time:
func1();
func2(); // Only runs after func1 has completed.
A quick and dirty solution would be to use a timeout:
const logoAnimation = () => {
this.logo.classList.add('fade-out-zoom-out');
this.blueCnt.classList.add('fade-out');
this.circWrapper.classList.add('dashed-border');
this.clickAbove.classList.add('text-fade-out');
this.welcome.classList.add('text-fade-in');
setTimeout(()=>{
rotateBorder();
}, 3000);
}

How can I assert a function has `await`ed an async function using should.js

I have an async function f that calls another async function g. To test if f calls g, I'm stubbing g using sinon and assert it's been called using should.js.
'use strict';
require('should-sinon');
const sinon = require('sinon');
class X {
async f(n) {
await this.g(n);
// this.g(n); // I forget to insert `await`!
}
async g(n) {
// Do something asynchronously
}
}
describe('f', () => {
it('should call g', async () => {
const x = new X();
sinon.stub(x, 'g').resolves();
await x.f(10);
x.g.should.be.calledWith(10);
});
});
But this test passes even when I forget to use await when calling g in f.
One of the ways to catch this error is to make the stub return a dummy promise and check if its then is called.
it('should call g', async () => {
const x = new X();
const dummyPromise = {
then: sinon.stub().yields()
};
sinon.stub(x, 'g').returns(dummyPromise);
await x.f(10);
x.g.should.be.calledWith(10);
dummyPromise.then.should.be.called();
});
But this is a bit bothersome. Are there any convenient ways to do this?
Your example for f shows flawed code design which becomes more obvious if you write the same function without async/await syntax:
f(n) {
return g(n).then(()=>{});
}
This achieves the same behavior - whether g resolved becomes hard to tell (assuming you don't know if f returned g's promise, which is the same as not knowing whether f awaited g). If f is not interested in the result of g it should just simply return it, not hide it. Then you can simply test for the result.
If your point is that f might have to trigger several async calls sequentially awaiting several g_1, g_2,... to resolve, then you can build a test chain by asserting in the stub of g_n+1 that the dummy-promise of g_n has been resolved. In general your approach to test a dummy-promise for its status is fine.
Instead of stubbing then, you're best off stubbing g in such a way that it sets some boolean on the next event loop iteration. Then, you can check this boolean after calling f to make sure f waited for it:
it('should call g', async () => {
const x = new X();
let gFinished = false;
sinon.stub(x, 'g').callsFake(() => {
return new Promise((resolve) => {
setImmediate(() => {
gFinished = true;
resolve();
});
});
});
await x.f(10);
x.g.should.be.calledWith(10);
gFinished.should.be.true();
});
Edit: Of course, this isn't a perfect guarantee because you could have f wait on any promise that waits at least as long as it takes for g to resolve. Like so:
async f(n) {
this.g(n);
await new Promise((resolve) => {
setImmediate(() => {
resolve();
});
});
}
This would cause the test I wrote to pass, even though it's still incorrect. So really it comes down to how strict you're trying to be with your tests. Do you want it to be literally impossible to have a false positive? Or is it ok if some obvious trickery can potentially throw it off?
In most cases I find that the latter is ok, but really that's up to you and/or your team.

Is using async in setTimeout valid?

I had a asynchronous function in Javascript and I added setTimeout to it. The code looks like that:
let timer;
clearTimeout(timer);
timer =setTimeout(() => {
(async() => {
await this._doSomething();
})();
}, 2000);
The purpose of setTimeout is to add 2 seconds before function will be run. It is to be sure that user stopped typing.
Should I remove async/await from this function now, since setTimeout is asynchronous anyway?
setTimeout adds a delay before a function call, whereas async/await is syntactic sugar ontop of promises, a way to chain code to run after a call completes, so they're different.
setTimeout has terrible error-handling characteristics, so I recommend the following in all code:
let wait = ms => new Promise(resolve => setTimeout(resolve, ms));
and then never call setTimeout directly again.
Your code now becomes:
let foo = async () => {
await wait(2000);
await this._doSomething();
}
except foo waits for doSomething to finish. This is usually desirable, but without context it's hard to know what you want. If you meant to run doSomething in parallel with other code, I recommend:
async () => { await Promise.all([foo(), this._otherCode()]); };
to join and capture errors in the same place.
If you truly meant to fire and forget _doSomething and not wait for it, you can lose the await, but you should try/catch errors:
async () => {
let spinoff = async () => { try { await foo(); } catch (e) { console.log(e); } };
spinoff(); // no await!
}
But I don't recommend that pattern, as it's subtle and can be easy to miss.
/* contrived example alert */
var foo = 'poo';
function setFoo(callback) (
setTimeout(function(){
foo = 'bar';
callback();
}, 100);
);
setFoo(function() {
alert(foo);
});

Asserting function calls inside a promise

I'm writing some tests for an async node.js function which returns a promise using the Mocha, Chai and Sinon libraries.
Let's say this is my function:
function foo(params) {
return (
mkdir(params)
.then(dir => writeFile(dir))
)
}
mkdir & writeFile are both async functions which return promises.
I need to test that mkdir is being called once with the params given to foo.
How can I do this?
I've seen quite a few examples on how to assert the overall return value of foo (sinon-as-promised is super helpful for that) but non about how to make sure individual functions are being called inside the promise.
Maybe I'm overlooking something and this is not the right way to go?
mkdir isn't called asynchronously here, so it's rather trivial to test:
mkdir = sinon.stub().resolves("test/dir")
foo(testparams)
assert(mkdir.calledOnce)
assert(mkdir.calledWith(testparams))
…
If you want to test that writeFile was called, that's only slightly more complicated - we have to wait for the promise returned by foo before asserting:
… // mdir like above
writeFile = sinon.stub().resolves("result")
return foo(testparams).then(r => {
assert.strictEqual(r, "result")
assert(writeFile.calledOnce)
assert(writeFile.calledWith("test/dir"))
…
})
You can mock the mkdir function then use setTimeout to check whether or not this function is called.
describe('foo', () => {
it('should call mkdir', () => {
return new Promise((resolve, reject) => {
let originalFunction = mkdir;
let timer = setTimeout(() => {
reject(`mkdir has not been called`);
});
mkdir = (...params) => new Promise(mkdirResolve => {
//restore the original method as soon as it is invoked
mkdir = originalMethod;
clearTimeout(timer);
mkdirResolve(someExpectedData);
resolve();
});
foo(params);
});
});
});

Categories