Skip to content
Advertisement

Can I rethrow a rejected await function, and catch it immediately

I’d like to catch all my exceptions in one place, but I can’t do that currently:

There is an important thing to note if you like more try/catch. The following code won’t catch the error:
[…]
Remember: a rejected Promise will propagate up in the stack unless you catch it. To catch the error properly in try/catch you would refactor like so:

whatever().catch(err => console.error(err));

But here is my code as I would like to have it:

async function getMtgJsonVersion() {
  try {
    const response = await axios(metaUrl).catch((err) => { throw err; });
    const { data: { meta: { version } } } = response;
    return version;
  } catch (error) {
    console.error(`Could not fetch MTG JSON metadata: ${error}`);
    throw error;
  }
}

and my test:

// console.error is mocked higher up in the test file
it.only('handles errors in fetching', async () => {
  expect.assertions(2);
  axios.mockReset(); // I use mockImplementationOnce elsewhere
  axios.mockRejectedValueOnce(new Error('bang'));
  expect(() => getMtgJsonVersion()).rejects.toThrow('bang');
  expect(console.error).toHaveBeenCalledWith(expect.stringContaining('bang'));
});

But when I run it, I get that the last expect is not fulfilled?

expect(jest.fn()).toHaveBeenCalledWith(...expected)  
   Expected: StringContaining "bang"  
   Number of calls: 0  

I was hoping to catch all my throws in one place, but it looks like it’s not as simple as I thought.

Is this possible, and how?

Answer

Because expect(fn).rejects.* is an asynchronous action, then it will take “a little time” to finish.

In your code, expect(console.error).toHaveBeenCalledWith(expect.stringContaining('bang')) will run before expect(() => getMtgJsonVersion()).rejects.toThrow('bang'); line. At that time, the console.log is not be called yet.

To make it work as your expectation, you have to wait until getMtgJsonVersion finishes, then assert on the log function. rejects.toThrow('bang') return a promise, then just wait for it with await keyword:

await expect(() => getMtgJsonVersion()).rejects.toThrow('bang');
expect(console.error).toHaveBeenCalledWith(expect.stringContaining('bang'));

My note: Avoid using try/catch in “child” unit, just use it in the “final parent” function, if you just want to log when the http request is failed:

async function getMtgJsonVersion() {
  const { data } = await axios(metaUrl).catch((error) => {
    console.error(`Could not fetch MTG JSON metadata: ${error}`);
    throw error;
  });
  return data.meta.version.
}
Advertisement