How to check the response from global fetch in JEST test case - node.js

So, i am using jest for testing my node function which is calling fetch() APi to get the data, now when I am writing the test cases for the same i am getting an error like :
expect(received).resolves.toEqual()
Matcher error: received value must be a promise
Received has type: function
Received has value: [Function mockConstructor]
my function :
export function dataHandler (req, res, next) {
const url= "someURL"
if (url ) {
return fetch(url )
.then((response) => response.json())
.then((response) => {
if (response.data) {
console.log(response);
res.redirect(somewhere`);
} else {
throw Error(response.statusText);
}
})
.catch((error) => {
next(error);
});
}
}
testcase :
it('check if fetch returning the response', async () => {
// Setup
const req = jest.fn(),
res = { redirect: jest.fn() },
next = jest.fn();
global.fetch = jest.fn().mockImplementation(() => {
return new Promise((resolve) =>
resolve({
json: () => {
return { data: "hello"};
}
})
);
});
await middlewares.dataHandler(req, res, next);
// Assert
expect(global.fetch).resolves.toEqual({ data: "hello" });
});
Please be advised I am not using any mocking API, and would prefer not to.
Can anyone help me with what's going wrong?

.resolves can only be used with a Promise.
global.fetch is a function so Jest throws an error.
If you are trying to assert that the Promise returned by calling global.fetch resolves to an object with a json function that returns { data: 'hello' } then you can do this:
expect((await global.fetch()).json()).toEqual({ data: 'hello' }); // Success!
...but I suspect that you are really trying to verify that response.data existed and that res.redirect was called with 'somewhere' in which case your assertion should just be this:
expect(res.redirect).toHaveBeenCalledWith('somewhere'); // Success!

Related

Using Firebase Cloud Functions to fetch data from an external API (Timeout)

This is my code:
const functions = require('firebase-functions');
const axios = require('axios');
const cors = require('cors')({ origin: true });
exports.apistatus = functions
.region('europe-west1')
.https.onRequest((req, res) => {
cors(req, res, () => {
if (req.method !== "GET") {
return res.status(401).json({
message: "Not allowed"
});
}
return axios.get('https://api.bsmsa.eu/ext/api/bsm/gbfs/v2/en/station_status')
.then(response => {
console.log(response.data);
return res.status(200).json(
response.data
)
})
.catch(err => {
return res.status(500).json({
error: err
})
})
})
// ! res.end();
});
I'm already using the Blaze Plan but I always get this when I invoke it >> Function execution took 60002 ms, finished with status: 'timeout'.
If I try to terminate the function like the documentation says with res.send() or res.end() I get this error: Error: Can't set headers after they are sent.
It works fine when I try to fetch other APIs.
You need a return keyword before the cors() function call.
exports.apistatus = functions
.region('europe-west1')
.https.onRequest((req, res) => {
return cors(req, res, () => { // <------ RETURN NEEDED HERE
if (req.method !== "GET") {
return res.status(401).json({
message: "Not allowed"
});
}
return axios.get('https://api.bsmsa.eu/ext/api/bsm/gbfs/v2/en/station_status')
.then(response => {
console.log(response.data);
return res.status(200).json(
response.data
)
})
.catch(err => {
return res.status(500).json({
error: err
})
})
})
});
Your axios code is being executed, and I suspect it's receiving a response, but because you aren't returning the result from CORS, the "parent" cloud function never knows the task has finished.
This example shows a sample from the official Firebase GitHub where they're using Cors.

Res.Redirect is not getting called in Jest Test

i am using node-express and am trying to check the redirect when the call from API is successful. I am getting an error that - Expected mock function to have been called, but it was not called.
here is my function:
export function globalAuthHandler (req, res, next) {
const global_signin_url = config.get('url');
if (global_signin_url) {
console.log(global_signin_url);
fetch(global_signin_url)
.then((response) => response.json())
.then((response) => {
console.log('Response', response);
if (response.data) {
console.log('Success!!!');
res.redirect('/signIn');
} else {
console.log('going here 1' + response);
res.redirect('/session-expired');
throw Error(response.statusText);
}
})
.catch((error) => {
console.log('going global here 2 ' + error);
next(error);
});
} else {
console.log('going here 3');
res.redirect('/session-expired');
}
}
here is the test :
it('should throw the error and redirect if the API fails with 404.', async () => {
// Setup
Config.get = jest.fn();
Config.get.mockReturnValue(true);
Config.initialize = jest.fn(() => Promise.resolve({ data: {} }));
const req = jest.fn(),
res = { redirect: jest.fn() },
next = jest.fn();
//global.fetch = jest.fn(() => new Promise((resolve) => resolve({ response: { ok: false, status: 404 } })));
global.fetch = jest.fn(
() =>
new Promise((resolve) =>
resolve({
json: () => {
return { };
}
/* data: { ok: true } */
})
)
);
// Act
await middlewares.globalAuthHandler(req, res, next);
// Assert
expect(res.redirect).toHaveBeenCalled();
expect(res.redirect).toHaveBeenCalledWith('/signIn');
});
I am not able to figure out that - even after going to the success!!! log, redirect is not getting triggered.
Calling await on middlewares.globalAuthHandler doesn't wait for it to complete since it isn't returning the Promise.
Return the Promise created by fetch:
export function globalAuthHandler (req, res, next) {
...
return fetch(global_signin_url) // <= return the Promise
...
}
...and the test will wait for the Promise to resolve before continuing to the expect statements.
That will give res.redirect a chance to be called before it is tested.

How to properly stub out request-promise in a mocha unit test using sinon?

My unit test is:
describe.only("Validator Service Tests", function () {
let request
before((done) => {
request = sinon.stub()
done()
})
beforeEach(() => {
process.env.API_URL = "http://test"
})
it('Should return with no errors if the file matches the schema', () => {
const updateStatusSpy = sinon.spy(FileLib, 'updateStatus')
request.yields({message: 'ok'})
return ValidatorService.handleMessage({
file: 'test'
})
.then((response) => {
assert()
console.log(response)
sinon.assert.calledOnce(updateStatusSpy)
assert(response, 'f')
})
})
})
The problem is my handleMessage function, which looks like:
exports.handleMessage = (message, done) => {
return stuff()
.then((result) => {
console.log('result', result)
if(result) {
return FileLib.updateStatus(fileId, 'valid')
}
return FileLib.updateStatus(fileId, 'invalid')
})
.then(done)
}
And my updateStatus function:
exports.updateStatus = function(fileId, status) {
console.log(fileId, status)
return request.put({
uri: `${process.env.API_URL}/stuff/${fileId}`,
body: {
status: status
}
})
}
My actual request call is buried so deep in, how can I stub it out when testing?
I'm not sure I completely understand your question, but if you are just trying to stub put, try something like this:
let stub;
beforeEach(() => {
putStub = sinon.stub(request, 'put').resolves('some_val_or_object'); //or yields or callsFake, depending on what you're using
});
it('should call request with put', async () => {
await //call your code
expect(putStub.called).to.be.true;
expect(putStub.calledWith(whatever_you_want_to_check)).to.be.true;
});

Unit test promise result inside Event Listener in Node

I have the following code that I want to test.
emitter.on("request", function(req, res) {
mock_finder.getMockedResponse().then((mockedResponse) => {
res.end(mockedResponse);
});
});
Then, I have this unit test.
it("should return mocked response", () => {
// given
const mockedResponse = {
success: true
};
mock_finder.getMockedResponse.mockImplementation(() => Promise.resolve(mockedResponse));
const res = {
end: jest.fn()
}
// when
emitter.emit('request', req, res);
// then
expect(res.end).toHaveBeenCalledWith(mockedResponse);
});
This test is not working because res.end(mockedResponse); is executed after the test finishes.
How can I test the promise response after an event is called?
Given there's no real async code going on here, you can verify the result on the next tick:
if('should return mocked response', done => {
...
emitter.emit('request', req, res);
process.nextTick(() => {
expect(res.end).toHaveBeenCalledWith(mockedResponse);
done()
});
})

How to test response data from Express in Jest

I'm writing unit tests for separate middleware functions in Node/Express using Jest.
A simple example of the middleware:
function sendSomeStuff(req, res, next) {
try {
const data = {'some-prop':'some-value'};
res.json(data);
next();
} catch (err) {
next(err);
}
}
And a sample of my test suite:
const httpMocks = require('node-mocks-http');
const { sendSomeStuff } = require('/some/path/to/middleware');
describe('sendSomeStuff', () => {
test('should send some stuff', () => {
const request = httpMocks.createRequest({
method: 'GET',
url: '/some/url'
});
let response = httpMocks.createResponse();
sendSomeStuff(request, response, (err) => {
expect(err).toBeFalsy();
// How to 'capture' what is sent as JSON in the function?
});
});
});
I have to provide a callback to populate the next parameter, which is called in the function. Normally, this would 'find the next matching pattern', and pass the req and res objects to that middleware. However, how can I do this in a test set-up? I need to verify the JSON from the response.
I don't want to touch the middleware itself, it should be contained in the test environment.
Am I missing something here?
Found a fix!
Leaving this here for someone else who might struggle with the same.
When returning data using res.send(), res.json() or something similar, the response object (from const response = httpMocks.createResponse();)
itself is updated. The data can be collected using res._getData():
const httpMocks = require('node-mocks-http');
const { sendSomeStuff } = require('/some/path/to/middleware');
describe('sendSomeStuff', () => {
test('should send some stuff', () => {
const request = httpMocks.createRequest({
method: 'GET',
url: '/some/url'
});
const response = httpMocks.createResponse();
sendSomeStuff(request, response, (err) => {
expect(err).toBeFalsy();
});
const { property } = JSON.parse(response._getData());
expect(property).toBe('someValue');
});
});
});
I did a different way by utilising jest.fn(). For example:
if you wanna test res.json({ status: YOUR_RETURNED_STATUS }).status(200);
const res = {};
res.json = jest.fn(resObj => ({
status: jest.fn(status => ({ res: { ...resObj, statusCode: status }
})),
}));
Basically, I mock the res chain methods(json and status).
That way you can do expect(YOUR_TEST_FUNCTION_CALL).toEqual({ res: { status: 'successful', statusCode: 200 }}); if your response structure is like that.

Resources