3

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. 
3
  • I would suggest that you give spyOn a try like in const spy = jest.spyOn(userArray, 'filter').mockImplementation(() => throw new Error()). jestjs.io/docs/en/jest-object#jestspyonobject-methodname Commented May 15, 2019 at 18:31
  • I've setup the test repl.it/repls/MedicalIntelligentTriangles and it seems to be working fine Commented May 16, 2019 at 6:30
  • @Teneff hmmm that is strange. The repl.it example you posted above seems to work for me as well. However, I still get an error when I run this test locally :( I have edited the post to include the error I am getting. Commented May 17, 2019 at 20:53

2 Answers 2

5

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you, that did the trick! Great explanation. Only thing I tweaked was the mocking of the function. userArray.filter = jest.fn().mockImplementation(() => {throw new Error()});
1

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.