Jest: Matcher error: expected value must be a function - unit-testing

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.

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 mock twilio - how to?

I have been using Jest to do my unit tests with node.
I am used to mocking the first level of the modules/functions, but on the challenge to mock Twilio, I am not having so much luck.
I am using the twilio method: client.messages.create, so here I have the twilio client from the constructor require('twilio')(account sid, token), and the first layer is from the object/method(?) messages, and last the third level create, and it's this last guy that I am trying to mock.
I was trying something like this:
jest.mock('twilio', () => {
const mKnex = {
messages: jest.fn(),
};
return jest.fn(mKnex);
});
However, I am not able to mock the client resolved value, where I get client.message.create is not a function.
If I try the above mock plus this client.messages.create.mockReturnValueOnce({sid: "FOO", status: "foo"); I get that cannot read the property create from undefined(messages).
Any tip, post, docs that could give me some luck on this?
Thanks
The solution for this is:
Create a file for Twilio client:
// sms.client.ts
import { Twilio } from 'twilio';
const smsClient = new Twilio(
'TWILIO-ACCOUNT-SID',
'TWILIO-TOKEN'
);
export { smsClient };
Then, your service file should look like this:
// sms.service.ts
import { smsClient } from './sms.client';
class SMSService {
async sendMessage(phoneNumber: string, message: string): Promise<string> {
const result = await smsClient.messages.create({
from: '(555) 555-5555',
to: phoneNumber,
body: message,
});
if (result.status === 'failed') {
throw new Error(`Failed to send sms message. Error Code: ${result.errorCode} / Error Message: ${result.errorMessage}`);
}
return result.sid;
}
}
export const smsService = new SMSService();
Last but not least, your spec/test file needs to mock the client file. E.g.
// sms.service.spec.ts
import { MessageInstance, MessageListInstance } from 'twilio/lib/rest/api/v2010/account/message';
import { smsClient } from './sms.client';
import { smsService } from './sms.service';
// mock the client file
jest.mock('./sms.client');
// fixture
const smsMessageResultMock: Partial<MessageInstance> = {
status: 'sent',
sid: 'AC-lorem-ipsum',
errorCode: undefined,
errorMessage: undefined,
};
describe('SMS Service', () => {
beforeEach(() => {
// stubs
const message: Partial<MessageListInstance> = {
create: jest.fn().mockResolvedValue({ ...smsMessageResultMock })
};
smsClient['messages'] = message as MessageListInstance;
});
it('Should throw error if response message fails', async () => {
// stubs
const smsMessageMock = {
...smsMessageResultMock,
status: 'failed',
errorCode: 123,
errorMessage: 'lorem-ipsum'
};
smsClient.messages.create = jest.fn().mockResolvedValue({ ...smsMessageMock });
await expect(
smsService.sendMessage('(555) 555-5555', 'lorem-ipsum')
).rejects.toThrowError(`Failed to send sms message. Error Code: ${smsMessageMock.errorCode} / Error Message: ${smsMessageMock.errorMessage}`);
});
describe('Send Message', () => {
it('Should succeed when posting the message', async () => {
const resultPromise = smsService.sendMessage('(555) 555-5555', 'lorem-ipsum');
await expect(resultPromise).resolves.not.toThrowError(Error);
expect(await resultPromise).toEqual(smsMessageResultMock.sid);
});
});
});
I've found a solution. It's still calling the endpoint, but for each twilio account, you get a test SID and Token, I used this one so it does not send a sms when testing with this:
if (process.env.NODE_ENV !== 'test') {
client = require('twilio')(accountSid, authToken)
listener = app.listen(3010, function(){
console.log('Ready on port %d', listener.address().port)
})
}else{
client = require('twilio')(testSid, testToken)
}

JEST trying to test an async function. Received error Can not use keyword 'await' outside an async function (34:15)

Ok Here is my code:
routes.test.js
import cisProvider from "./cognito-provider";
test ('User' , () => {
expect.assertions(1);
let data = await xcisProvider.forgoPassword({
ClientId: '2fpfiodf5ppsqg6tnndfnkl5r',
UserName: 'naman.jain#xe.com'
}
);
expect(data.code_delivery_details.DeliveryMedium).toEqual("EMAIL");
});
And here is what function I am trying to access
cognito-provider.js
class CognitoProvider {
constructor(config) {}
forgotPassword = params => {
const { Username: username, ClientId: clientId } = params;
return this.getHashedClientSecret(username, clientId)
.then(clientSecretHash => {
params = Object.assign(params, {
SecretHash: clientSecretHash
});
return this.provider.forgotPassword(params).promise();
});
};
}
export default CognitoProvider;
I recieve the following error when perform the test run
SyntaxError: routes.test.js: Can not use keyword 'await' outside an async function (34:15)
The line it refers to is :
let data = await xcisProvider.forgoPassword({ ...

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)
}

How to test Promise catch with Mocha

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();
}
);
});