How to test Promise catch with Mocha - unit-testing

I'm trying to test the GET HTTP method from a requests module:
const get = (host, resource, options) => {
...
return new Promise((resolve, reject) => fetch(url, opts)
.then(response => {
if (response.status >= 400) {
reject({
message: `[API request error] response status: ${response.status}`,
status: response.status });
}
resolve(response.json());
})
.catch(error => reject(error)));
};
And here is how I tested the .then part:
it('Wrong request should return a 400 error ', (done) => {
let options = { <parameter>: <wrong value> };
let errorJsonResponse = {
message: '[API request error] response status: 400',
status: 400,
};
let result = {};
result = get(params.hosts.api, endPoints.PRODUCTS, options);
result
.then(function (data) {
should.fail();
done();
},
function (error) {
expect(error).to.not.be.null;
expect(error).to.not.be.undefined;
expect(error).to.be.json;
expect(error).to.be.jsonSchema(errorJsonResponse);
done();
}
);
});
However I didn't find a way to test the catch part (when it gives an error and the response status is not >= 400).
Any suggestions?
It would also help me solve the problem a simple example with another code that tests the catch part of a Promise.

I've ended up writing the following code in order to test the catch:
it('Should return an error with invalid protocol', (done) => {
const host = 'foo://<host>';
const errorMessage = 'only http(s) protocols are supported';
let result = {};
result = get(host, endPoints.PRODUCTS);
result
.then(
() => {
should.fail();
done();
},
(error) => {
expect(error).to.not.be.null;
expect(error).to.not.be.undefined;
expect(error.message).to.equal(errorMessage);
done();
}
);
});

Related

TypeError: Cannot read properties of undefined (reading 'address')

I am trying to test my fastify application using supertest and chai but I got the typeError, But I run the same code using express its running.
My app.js
const PORT = 5000
fastify.post('/api/users', function (req, res) {
const { name } = req.body.name
// const { email } = req.body.email
console.log(name, email)
res.send('User created successfully')
})
const app = fastify.listen(PORT, (err) => {
if (err) {
console.log("We Got an error in listening " + err)
process.exit(1)
}
})
module.exports = app;
My test File:
const request = require('supertest');
const { expect } = require('chai');
const app = require('../app');
describe('POST /api/users', () => {
it('should process the form data', (done) => {
request(app)
.post('/api/users')
.send({ name: 'bar' })
.expect(200)
.end((err, res) => {
if (err) return done(err);
// console.log(res, "***********");
// expect(res.body).equal({ name: 'bar' });
expect(res.text).to.equal('User created successfully');
done();
});
});
});
Getting this error:
TypeError: Cannot read properties of undefined (reading 'address')
at Test.serverAddress (node_modules\supertest\lib\test.js:46:22)
at new Test (node_modules\supertest\lib\test.js:34:14)
at Object.obj.<computed> [as post] (node_modules\supertest\index.js:43:18)
at Context.<anonymous> (test\test-pages.js:85:8)
at processImmediate (node:internal/timers:466:21)
Instead of using
'pat = "\n"'
as the argument use
sep = "\n"
Your line of code should look like
df.Reviews.str.split(sep="\n", expand=True)

Jest: Matcher error: expected value must be a function

I'm using NestJS with Jest and getting Matcher error: expected value must be a function error when run following unit test. I have set invalid email in mockBody. Did I missed anything here?
app.service.ts
#Injectable()
export class UserService {
constructor(private emailService: EmailService) {}
async registerUserInquiry(user: UserDto): Promise<{ email: string }> {
try {
await sendEmail(user);
} catch (error) {
throw new HttpException('Something went wrong!', HttpStatus.BAD_REQUEST);
}
return {
email: user.email,
};
}
}
app.service.spec.ts
describe("registerUser()", () => {
it("Should throw bad request error when passing invalid data", async () => {
const mockBody: UserDto = {
name: "John Doe",
message: "Example inquiry message",
email: "#example",
mobile: "+60121234567",
};
expect(async () => await service.registerUserInquiry(mockBody)).toThrow(
new HttpException("Something went wrong!", HttpStatus.BAD_REQUEST)
);
});
});
email.config.ts
export const sendEmail = async (user: User) => {
const transporter = nodemailer.createTransport({
... // service & auth
});
const options = {
... // email info
};
await transporter.sendMail(options, function (error, info) {
try {
console.info(error);
return info;
} catch (error) {
console.error(error);
throw error;
}
});
};
Error:
Instead of this
expect(async () => await service.registerUserInquiry(mockBody)).toThrow(
new HttpException("Something went wrong!", HttpStatus.BAD_REQUEST)
);
Try this one
await except(service.registerUserInquiry(mockBody)).rejects.toThrowError(...)
Your function is a promise which means it is not throwing an error but instead it rejects.

Jest - Mock promisified SQS calls

Let's assume that I have a QueueClass with a method send, that gets some data as parameter
which then sends to a SQS queue.
I want to write 2 tests:
One that tests that the MessageBody and QueueUrl keys have the expected values passed in.
One that in case of error an exception gets thrown.
The method to be tested looks like this:
My send method:
async send(data) {
return SQS.sendMessage({
MessageBody: JSON.stringify(data),
QueueUrl: 'queue_url_here',
})
.promise()
.catch((error) => {
// Throw exception ...
});
}
The test I have for that method:
const aws = require('aws-sdk');
jest.mock('aws-sdk', () => {
const SQSMocked = {
sendMessage: jest.fn().mockReturnThis(),
promise: jest.fn(),
};
return {
SQS: jest.fn(() => SQSMocked),
};
});
sqs = new aws.SQS();
test('my test', async () => {
const data = {};
await QueueClass.send(data);
expect(sqs.sendMessage).toHaveBeenCalledWith({
MessageBody: JSON.stringify(data),
QueueUrl: 'queue_url_here',
});
});
That test gives me the following error:
TypeError: Cannot read property 'catch' of undefined
I did try adding catch: jest.fn() to the SQSMocked object, the exact same way I do with promise, but kept getting the same error.
The thing is that when I change the method that I am trying to test so it uses try-catch block instead of .promise() and .catch() :
async send(data) {
try {
return SQS.sendMessage({
MessageBody: JSON.stringify(data),
QueueUrl: 'queue_url_here',
});
} catch (error) {
// Throw exception ...
}
}
my test passes, so that makes me think that this is not necessarily an issue about properly mocking the SQS.
Any ideas why when using .promise() and .catch() my test fails ?
Also how could I test a case where an Error gets thrown by the queue ?
I would like to be able to do something like this:
await expect(sqs.sendMessage)
.resolves
.toEqual(...);
OR
await expect(sqs.sendMessage)
.rejects
.toThrowError(new Error('Some error thrown.'));
promise is stubbed and returns undefined, this is the reason why it doesn't return a promise that could be chained. It's supposed to return a promise, as the name suggests.
Since values may be different in different tests, it's better to expose it as a variable. sendMessage can be exposed as well for assertions:
const mockPromiseFn = jest.fn();
const mockSendMessage = jest.fn().mockReturnThis();
jest.mock('aws-sdk', () => {
return {
SQS: jest.fn().mockReturnValue({
sendMessage: mockSendMessage,
promise: mockPromiseFn
})
};
});
It doesn't make sense to test it with await expect(sqs.sendMessage).rejects... because it tests the code you've just written.
It likely should be:
mockPromiseFn.mockRejectedValue(new Error(...));
await expect(QueueClass.send(data)).rejects.toThrowError(...);
expect(mockSendMessage).toBeCalledWith(...);
This is potentially a mistake:
async send(data) {
try {
return SQS.sendMessage(...);
} catch (error) {
// Throw exception ...
}
}
try..catch is unable to catch asynchronous errors from async return, also sendMessage return value wasn't converted to a promise.
It should be:
async send(data) {
try {
return await SQS.sendMessage(...).promise();
} catch (error) {
// Throw exception ...
}
}
duplicate questions
How to mock AWS sqs call for unit testing
just add the method around to be called like so...
class EventService {
static async sendFifoMessage(
url,
message,
groupId,
dedupeId,
) {
const sqsMessageRequest = {
QueueUrl: url,
MessageBody: JSON.stringify(message),
MessageGroupId: groupId,
};
if (!!dedupeId) {
sqsMessageRequest.MessageDeduplicationId = dedupeId;
}
return await new SQS().sendMessage(sqsMessageRequest).promise();
}
}
import AWS = require('aws-sdk');
const URL = 'URL';
const MESSAGE = 'MESSAGE';
const GROUP_ID = 'GROUP_ID';
const DEDUPE_ID = 'DEDUPE_ID';
const BAD_REQUEST = 'BAD_REQUEST';
jest.mock('aws-sdk', () => {
const SQSMocked = {
sendMessage: jest.fn().mockReturnThis(),
promise: jest.fn(),
};
return {
SQS: jest.fn(() => SQSMocked),
};
});
const sqs = new AWS.SQS({
region: 'us-east-1',
});
describe('EventService', () => {
beforeEach(() => {
(sqs.sendMessage().promise as jest.MockedFunction < any > ).mockReset();
});
afterAll(() => {
jest.clearAllMocks();
jest.restoreAllMocks();
jest.resetAllMocks();
});
describe('sendFifoMessage', () => {
const messageResult = {
QueueUrl: URL,
MessageBody: JSON.stringify(MESSAGE),
MessageGroupId: GROUP_ID,
MessageDeduplicationId: DEDUPE_ID,
};
it('sendMessage successfully', async() => {
(sqs.sendMessage().promise as jest.MockedFunction < any > ).mockResolvedValueOnce('mocked data');
await EventService.sendFifoMessage(URL, MESSAGE, GROUP_ID, DEDUPE_ID);
expect.assertions(2);
expect(sqs.sendMessage).toBeCalledWith(messageResult);
expect(sqs.sendMessage().promise).toBeCalledTimes(1);
});
it('sendMessage throws', async() => {
(sqs.sendMessage().promise as jest.MockedFunction < any > ).mockRejectedValueOnce(BAD_REQUEST);
expect(async() => await EventService.sendFifoMessage(URL, MESSAGE, GROUP_ID, DEDUPE_ID)).rejects.toThrowError(
new Error(BAD_REQUEST),
);
expect(sqs.sendMessage).toBeCalledWith(messageResult);
expect(sqs.sendMessage().promise).toBeCalledTimes(1);
});
});
});

How to test async function with axios?

I need to test async function using mocha.
Tried to test function that returns Promise from axios. Looked through many examples with axios-mock-adapter to solve my issue. BUT: axios sends REAL request, not mock as expected.
describe ('login sendRequest', () => {
let sandbox = null;
before(() => {
sandbox = sinon.createSandbox();
});
after(() => {
sandbox.restore();
});
it('should create and return REST promise', done => {
const mockAdapter = new MockAdapter(axios);
const data = { response: true };
mockAdapter.onAny('http://google.com').reply(200, data);
const requestParams = {
method: 'post',
url: 'http://google.com',
data: {},
adapter: adapter,
};
logic.sendRequest(requestParams).then(response => {
console.log(response);
done();
}).catch(err => {
console.log(err);
});
});
});
logic.js
export async function sendRequest(requsetParams) {
return await requestSender.request(requsetParams);
}
Expected to get 200 response and mock data that was set before. Why I don't get the response I need? May someone help?

Testing catch block via jest mock

I'm trying to test the 'catch' block of an async redux action via jest, but throwing a catch in the mock causes the test as a whole to fail.
My action is as follows:
export function loginUser(username, password) {
return async dispatch => {
dispatch({type: UPDATE_IN_PROGRESS});
try {
let response = await MyRequest.postAsync(
'/login', {username: username, password: password}
);
dispatch({
type: USER_AUTHENTICATED,
username: response.username,
token: response.token,
role: response.role,
id: response.id
});
} catch (error) {
dispatch({type: USER_SIGNED_OUT});
throw error;
} finally {
dispatch({type: UPDATE_COMPLETE});
}
};
}
The test is trying to mock up 'MyRequest.postAsync' to throw an error and thus trigger the catch block, but the test just bails with a 'Failed' message
it('calls expected actions when failed log in', async() => {
MyRequest.postAsync = jest.fn(() => {
throw 'error';
});
let expectedActions = [
{type: UPDATE_IN_PROGRESS},
{type: USER_SIGNED_OUT},
{type: UPDATE_COMPLETE}
];
await store.dispatch(userActions.loginUser('foo', 'bar'));
expect(store.getActions()).toEqual(expectedActions);
});
Is there a way to trigger the catch block to execute in my test via a jest mock function (or any other way for that matter)? Would be annoying to not be able to test a large chunk of code (as all my requests work in the same way).
Thanks in advance for help with this.
I don't know if it's still relevant, but you can do it in this way:
it('tests error with async/await', async () => {
expect.assertions(1);
try {
await store.dispatch(userActions.loginUser('foo', 'bar'));
} catch (e) {
expect(e).toEqual({
error: 'error',
});
}
});
Here is a documentation about error handling
I had the same issue. For me the below works. Wrapping up the await with a try/catch
it('calls expected actions when failed log in', async() => {
MyRequest.postAsync = jest.fn(() => {
throw 'error';
});
let expectedActions = [
{type: UPDATE_IN_PROGRESS},
{type: USER_SIGNED_OUT},
{type: UPDATE_COMPLETE}
];
try {
await store.dispatch(userActions.loginUser('foo', 'bar'));
} catch(e) {
expect(store.getActions()).toEqual(expectedActions);
}
});
I set the instance variable which we will access in our testing function to undefined so that it will go to catch block.
PS : This might not be possible all the times as we might not be having variables all time
class APIStore {
async fetchProductsAPI() {
try {
const products = networkManager.fetch('products')
this.productsStore.setProducts(prodcuts)
}
catch(e) {
this.apiStatus = API_FAILED
this.apiError = e
}
}
}
Test case
it('Check API Error ', async () => {
const toCheckErrorStore = new APIStore()
// Setting products store to undefined so that execution goes to catch block
toCheckErrorStore.productsStore = undefined
await toCheckErrorStore.fetchProductsAPI()
expect(toCheckErrorStore.apiStatus).toBe(API_FAILED)
expect(toCheckErrorStore.apiError).toEqual(errorObjectIWantToCompareWith)
}