My component throw error (and should be)
function ProblemChild() {
throw new Error("Error thrown from problem child");
return <div>Error</div>; // eslint-disable-line
}
My question is .. how can I test this??
const wrapper = mount(<ProblemChild />);
Something like this..
expect(wrapper).toThrow()
Wrap mounting within callback. Like so:
expect(() => mount(<ProblemChild />)).toThrow()
You can do something like this:
it('should throw an error when there is an error', () => {
try {
wrapper.setState({error: true});
expect(true).toBe(false); //Fail test if no error is thrown
} catch (error) {
expect(error).toBe(error); //Pass test if an error is thrown
}
});
In the example above i have the error in the state. It can be possible that your function is called onClick or onChange. In that case you have to use a simulate event before the assertion.
Related
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");
}
}
);
I have typescript class with some functions in it. Each function has a try catch block where upon hitting the catch it returns a pre defined response.
I am writing unit tests using mocha and chai and I am having trouble trying to explicitly hit the catch blocks.
For example, consider this simple function below
public async verifyCode(email: string, code: string) {
try {
let result: CodeInterface | null = //call db to get matching code
if(result === null) {
return {status: 401, message: INCORRECT_FELLOWSHIP_CODE_MESSAGE};
}
return result._id;
} catch (error) {
Logger.log(LoggerLogTypes.ERROR, {
class_name: "LaunchServiceImpl",
function_name: "verifyFellowshipCode",
message: error.message,
stack: error.stack,
additional_info: {
code
}
});
return false;
}
}
I want to write a test case where I can just send the control directly to the catch block to get the false value. This is a very simplistic example but in few other functions I am doing a lot more in the catch block.
My mocha unit test looks like this:
it("should go to the catch block and return false in case of exception", async function() {
let serviceInstance = new MyClass();
let response = await serviceInstance.verifyCode("john#test.com", "abc123");
// how do I directly jump to the catch block here??
expect(response).to.equal(false);
});
Suppose you have a function that will throw an error with the message User not found so you can test like this:
profileServiceInstance.getProfile('1').catch((err) => {
expect(() => {
throw err;
}).to.throw(Error, 'User not found');
});
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.
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.
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);