Avoid calling `expect` conditionally: jest testing error - jestjs

I'm trying to test an async code with a negative case when the response is rejected.
As per the docs, we can use try, catch with async await while trying to implement mock rejection https://jestjs.io/docs/tutorial-async
// Or using async/await.
it('tests error with async/await', async () => {
expect.assertions(1);
try {
await user.getUserName(1);
} catch (e) {
expect(e).toEqual({
error: 'User with 1 not found.',
});
}
});
I tried to do the same by implementing mock rejected value. But, I'm getting the following error: Avoid calling 'expect' conditionally. I've also added expect.assertions(), and the error remains.
I know that there's an eslintjest/no-conditional-expect that can be enabled, but that's a bad practice I don't want to follow.
My implementation
it("Should reject with with message, User already exists with same email", async () => {
expect.assertions(1);
try {
await signupService(userSignupData);
} catch (e) {
expect(e).toContain("User already exists");
}
});
Any help is appreciated. Thanks!
P.S. I saw a similar question but there was no solution.

You're running into a warning from the eslint-plugin-jest library. According to that library's documentation for no-conditional-expect, it looks like you will want to use a finally block, maybe something like this:
it(
"Should reject with with message, User already exists with same email",
async () => {
let err;
try {
await signupService(userSignupData);
} catch (e) {
err = e;
} finally {
expect(err).toContain("User already exists");
}
}
);

Related

Try catch block doesn`t catch error in node.js

class AuthController {
static methods = {
GET: {
'/auth/signup': {
func: AuthService.signUp,
response: (data, res) => {
res.statusCode = 200;
res.end(JSON.stringify(data));
},
},
},
};
static use(req, res) {
const route = this.methods[req.method][req.url];
if (!route) {
res.statusCode = 404;
res.end(JSON.stringify({ message: 'Not found 404!' }));
return;
}
try {
const data = JSON.parse(req?.body?.data || '{}');
const result = route.func({ ...data });
route.response(result, res);
} catch (err) {
console.log(err, 'here');
res.statusCode = err.statusCode || 500;
res.end(JSON.stringify(err.message));
}
}
}
class AuthService {
static async signUp({ login, password }) {
if (!login || !password) throw new BaseError(400, 'kl', 'Custom error');
}
}
It shows the error in console but try catch block doesn't see it.
Here is the traceback.
I don't know what the reason is because the function which throws error is inside of the block. Help please!
The trace back that you attached tells you exactly what the problem is and what you need to do:
This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch()
You can't catch an exception thrown by an async function with a try..catch block outside of that function, because script execution reaches the catch block before the async execution is finished. You therefor have to use .catch(..) instead:
const result = route.func({ ...data }).catch((err) => {
console.log("catched error: ", err);
});
I see one issue. You have declared signUp() to be async. That means it always returns a promise and it means that any throw operations inside it reject that promise that it returns (the exception doesn't propagate synchronously). But, when you attempt to call it here:
const result = route.func({ ...data });
You don't await it so when signUp() rejects, the promise goes into result, but nobody ever handles the fact that the promise rejected and you get UnhandlePromiseRejectionWarning from the system.
I can't see the whole overall design (of all the other routes), but perhaps you just need to add await to this:
const result = await route.func({ ...data });
And, you would have to make .use() be async also.
Or, if signUp() doesn't actually need to be async, then just remove the async from its declaration and the throw will be synchronous (instead of being turned into a rejected promise) and your try/catch will catch it then.

Struggling with calling a function that uses promises in node.js

I am struggling with some code... The 2 examples below I would think would work the same but the second example throws an error? I am also struggling to figure out the error, it's not bubbling up? Admittedly I am not a seasoned node developer so any guidance would be much appreciated! If it's relevant the create method in the module is calling the sequelize create.
This works
var p1 = deliverabiltyConfigs.create2(cfgObject);
return Promise.all([p1]).then(function([res1]) {
res.json({result: res1})
});
This does not
deliverabiltyConfigs.create2(cfgObject).then(res1 =>{
res.json({result: res1})
})
Here is the function that I am calling in a controller module
exports.create2 = (dConfig) => {
DeliverabilityConfig.create(dConfig)
.then(data => {
return data
})
.catch(err => {
return {
message:
err.message || "Some error occurred while createing this config."
};
});
};
The create2 function always returns null, so neither invocation will work. Promise.all([p1]) hides the problem, returning a promise to perform an array of no promises.
create2(cfgObject).then(res1 =>{ attempts to invoke then() on null, generating a more obvious error. But neither way works.
Fix by deciding which promise syntax you want, using each as follows:
Using original promise syntax....
exports.create2 = dConfig => {
// note the return
return DeliverabilityConfig.create(dConfig)
.catch(err => {
const message = err.message || "Some error occurred while createing this config.";
return { message };
});
};
// caller
deliverabiltyConfigs.create2(cfgObject).then(result =>{
res.json(result);
})
With recent syntactic sugar...
exports.create2 = async (dConfig) => {
try {
// its fine to not await here, since the caller will await
// but just to illustrate how you might perform more async work here...
return await DeliverabilityConfig.create(dConfig);
} catch (err) {
const message = err.message || "Some error occurred while createing this config."
return { message }
}
}
// caller
var result = await deliverabiltyConfigs.create2(cfgObject);
res.json(result);
Use Promise.all() to run >1 promise concurrently. You've only got one promise in the OP, so no reason for it here.

How to handle error in async/await function?

I have a function like this:
async getPatient(patientId: string): Promise<PatientDTO> {
const patient = await PatientDAO.getPatients({ id: patientId })
if (patient.length === 0) {
throw new NotFoundError("Patient Not Found!")
}
return patient[0]
}
But I got an error
UnhandledPromiseRejectionWarning: Error: Patient Not Found!
This happened cause I have used async function. How can I make this code running properly?
In order to manage errors in an async function, you have to use a try/catch block:
async getPatient(patientId: string): Promise<PatientDTO> {
try {
const patient = await PatientDAO.getPatients({ id: patientId })
return patient[0]
} catch (error) {
// Do whatever you may want with error
throw error;
}
}
I should mention, that if you simply want to throw the error thats received from getPatients theres no need for a try/catch block at all. Its only needed if you wish to modify the error or perform an extra action according to the error that was thrown.
You have 2 options:
First one is try/catch block with await keyword. Please notice that await has to be used in async function.
try {
const patient = await getPatient(foo);
// handle your data here
} catch(e) {
// error handling here
}
Second one is catch function
getPatient(foo)
.then(patient => {
// handle your data here
}).catch(error => {
// error handling here
});

NodeJS. Can I handle errors in one place? Without try and catch blocks

Node said that I should catch errors on all my promises.
Does exist a good practice for handling the same errors in one function?
I don't want to write always the same catch block for all requests.
My code looks like:
(async () => {
try {
await makeRequest()
}
catch(e) {
console.error(e)
}
})()
Try this :
makeRequest()
.catch(err => {
// console.error(err)
})
For async IIFE (IIAFE) error handling is performed as:
(async () => {
await makeRequest()
})().catch(console.error);
Notice that in Node.js console methods are bound to proper context, so they can be passed as callbacks.

Catching thrown errors with SinonJS

I've got a method that may throw an Error, but I'm having trouble writing a SinonJS/Mocha/Should unit test case for this condition.
Sample function under test:
function testError(value) {
if (!value) {
throw new Error('No value');
return false;
}
};
Sample test:
describe('#testError', function() {
it('throws an error', function() {
var spy = sinon.spy(testError);
testError(false);
spy.threw().should.be.true();
});
});
This outputs:
#testError
1) throws an error
0 passing (11ms)
1 failing
1) #testError throws an error:
Error: No value
at testError (tests/unit/js/test-error.js:6:14)
at Context.<anonymous> (tests/unit/js/test-error.js:14:6)
I was expecting Sinon to catch the Error and allow me to spy on the throw, but it seems to fail the test instead. Any ideas?
I referred to Don't sinon.js spys catch errors? but the only solution there is to use expect. I'd prefer to keep with a single assertion library if possible.
It appears that this works inside a try/catch:
function foo() { throw new Error("hey!"); }
var fooSpy = sinon.spy(foo);
try {
fooSpy();
} catch (e) {
// pass
}
assert(fooSpy.threw());
Note that you have to call fooSpy, not foo itself.
But also note that .should.be.true() is not part of Sinon, so you're probably already using Chai or a similar library, in which case the expect(foo).to.have.thrown() or assert.throws(foo, someError) syntax seems much nicer.
Update: If you're using ShouldJS, looks like you can use should.throws. I still think this is nicer than using the Sinon version for this purpose.
Revised
Following #nrabinowitz's helpful advice, here's a solution that uses should.throws. This avoids using Sinon.spy altogether.
describe('#testError', function() {
it('throws an error', function() {
should.throws(function() {
testError(false);
});
});
});
const bootstrap = async anyParam => {
if(!anyParam){
throw new Error('test')
}
await anyParam()
}
const spy = sinon.spy(bootstrap)
try {
await spy();
} catch (e) {
expect(e.message).to.be.equal('test')
spy.called = false
}
expect(spy.called).to.be.equal(false);

Resources