Reputation: 33
I've been stuck trying to create a specific test for a few days now, and would appreciate any insight into what I may be doing wrong.
I am trying to mock out the Array filter function to throw an error.
userHelper.js
//filter users by email ending
const filterUsersByEmailDomain = (usersArray, emailEnding) => {
try {
let bizUsers = usersArray.filter(user => {
return user.email.endsWith(emailEnding);
});
return bizUsers;
} catch (err) {
console.log('error filtering users. Throwing error.');
throw err;
}
}
userHelper.test.js:
it('should throw', () => {
const user1 = {id: 1, email: '[email protected]'};
const user2 = {id: 2, email: '[email protected]'};
const userArray = [user1, user2];
const domainEnding = '.biz';
Array.prototype.filter = jest.fn().mockImplementation(() => {throw new Error()});
expect(() => {usersHelper.filterUsersByEmailDomain(userArray, domainEnding)}).toThrow();
});
From what I can tell, the error is being thrown, but isn't successfully being caught. I've also tried making the called to usersHelper.filterUsersByEmailDomain() within a try catch block as i have seen others do, but was also unsuccessful. Thanks in advance!
Edit: Here is the error I receive when running this test setup locally in my project.
● Testing the usersHelper module › should throw
56 | const domainEnding = '.biz';
57 |
> 58 | Array.prototype.filter = jest.fn().mockImplementation(() => {throw new Error()});
| ^
59 |
60 | expect(() => {usersHelper.filterUsersByEmailDomain(userArray, domainEnding)}).toThrow();
61 | });
at Array.filter.jest.fn.mockImplementation (utils/__tests__/usersHelper.test.js:58:76)
at _objectSpread (node_modules/expect/build/index.js:60:46)
at Object.throwingMatcher [as toThrow] (node_modules/expect/build/index.js:264:19)
at Object.toThrow (utils/__tests__/usersHelper.test.js:60:87)
(node:32672) UnhandledPromiseRejectionWarning: Error
(node:32672) UnhandledPromiseRejectionWarning: Unhandled promise rejection. 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 .c
atch(). (rejection id: 2)
(node:32672) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
Upvotes: 3
Views: 25387
Reputation: 45850
Array.prototype.filter
is a very low-level function and mocking it to throw an error can cause your tests to not run properly.
Take this simple test:
it('should throw', () => {
expect(() => { throw new Error() }).toThrow(); // Success!
});
...which works fine...
...but mock Array.prototype.filter
to throw an error and it fails:
it('should throw', () => {
Array.prototype.filter = jest.fn(() => { throw new Error() });
expect(() => { throw new Error() }).toThrow(); // Fail!
});
Instead, just mock filter
on the array itself:
it('should throw', () => {
const user1 = { id: 1, email: '[email protected]' };
const user2 = { id: 2, email: '[email protected]' };
const userArray = [user1, user2];
const domainEnding = '.biz';
userArray.filter = () => { throw new Error() }; // <= mock filter on userArray
expect(() => { usersHelper.filterUsersByEmailDomain(userArray, domainEnding) }).toThrow(); // Success!
});
JavaScript looks for a property on the object itself before checking its prototype so the mock filter
on userArray
gets called in filterUsersByEmailDomain
and the test passes as expected.
Upvotes: 5
Reputation: 1047
You want to put your toThrow()
before the execution of the tested function, in Jest 'toX' means that it must be setup beforehand e.g: toBeCalled()
. This is why toHaveBeenCalled()
exists, as this form allows the assertion to happen after the code has run.
Upvotes: 1