Reputation: 2489
I want to implement a function which hits some apis asynchronously and does some calculations. However I would like to use promises (also because I thought its easy to write unit test for them)
This is the function I want to write.
const CalcFactory = (someApi1, someApi2, someApi3) =>
async function calculation(params){
return new Promise((res, rej) =>{
const data1 = someApi1.getData() // getData is async
someApi2.getData((err, data) =>{
if(err)
rej(err);
else
res();
});
});
}
The question now is how to test and mock this? My first test is if all the api methods are beeing called but I already struggle with that.
const Calculator = CalcFactory(api1Mock, api2Mock, api3Mock);
if('should hit all external apis', () => {
return Calculator(somedada).then(()=>{
expect(api1Mock.getData).toHaveBeenCalledWith(someData);
expect(api2Mock.getData).toHaveBeenCalledWith(somedata);
});
});
the mocks look like this:
const api1Mock = {
someData: jest.fn(),
}
const api2Mock = {
someData: jest.fn().mockResolvedValue({
data:{},
})
}
However I always get a timeout when I run the test. I am also not really sure if this is a good approach to write the function but I really would like to use a promise.
Upvotes: 1
Views: 558
Reputation: 222474
There's getData
in one place and someData
in another. Considering the promise is not rejected, it's not a problem. Calculator(somedada)
promise isn't resolved because someApi2.getData
is incorrectly mocked and results in pending promise.
It's an antipattern to return a promise from async
function without using await
. This means that either it doesn't benefit from being async
, or promise constructor contains too much besides promisfication of non-promise API.
In case the use of async
is justified, it can be:
async function calculation(params){
...
const data1 = someApi1.getData()
const data2 = await new Promise((res, rej) =>{
someApi2.getData((err, data) => {
if(err)
rej(err);
else
res();
});
});
...
}
In Node, error-first callbacks can be promisified with util.promisify
.
And mocked someApi2.getData
should use a callback like expected:
const api2Mock = {
getData: jest.fn().mockImplementation(cb => cb(null, 'data'))
}
If someApi2.getData
is used more than once, it makes sense to promisify it instead of using promise constructor every time.
Upvotes: 1