Jest: async/ await toThrow is not working

Created on 15 Sep 2016  ·  21Comments  ·  Source: facebook/jest

next test

async function check() {
  throw new Error('Test');
}

expect(async () => 
  await check()
).toThrow();

will fail and don't catch error properly (I am using latest version of jest)

Most helpful comment

Instead of

expect(async () => await check()).toThrow()
// Jest error: "Expected the function to throw an error. But it didn't throw anything."

this works:

expect(check()).rejects.toEqual(new Error('Test'))

Yeah the syntax is awkward...

All 21 comments

Yes, this isn't supported currently. See https://github.com/facebook/jest/issues/1377

Instead of

expect(async () => await check()).toThrow()
// Jest error: "Expected the function to throw an error. But it didn't throw anything."

this works:

expect(check()).rejects.toEqual(new Error('Test'))

Yeah the syntax is awkward...

You can also write:

let error;
try {
  await check();
} catch (e) {
  error = e;
}
expect(error).toEqual(new Error('Test'));

The following worked for me as well:

async function check() {
  throw new Error('Test');
}

expect(check()).rejects.toEqual(new Error('Test'))

See: https://facebook.github.io/jest/docs/en/tutorial-async.html#rejects

Adding to the list of workarounds.
If you like me want to catch a specific error type, and don't care about the message:

async function check() {
  throw new SomeSpecificError('Whatever');
}

await check()
  .then(() => {
    throw new Error('Should go to .catch, not enter .then');
  })
  .catch((err) => {
    expect(err).toBeInstanceOf(SomeSpecificError);
  });
await expect(check).rejects.toThrow(SomeSpecificError);

Should work as well

await expect(check).rejects.toThrow(/DUPLICATES_DETECTED/);

is what I do.

async function check() {
  throw new Error("Test");
}
await expect(check).rejects.toThrow(Error);
expect(received).rejects.toThrow()

received value must be a Promise.
Received:
 function: [Function check]

You need to invoke it to give it the actual promise

async function check() {
  throw new Error("Test");
}
await expect(check()).rejects.toThrow(Error);

Should work as well

it(`some test`, async () => {
  async function check() {
    try {
      return Promise.reject(await asyncMethod());
    } catch (error) {
      throw new Error("test");
    }
  }
  await expect(check()).rejects.toThrow(Error);
});

can work

version

  • jest: 23.4.2
  • ts-jest: 23.1.2

I know it need a promise, but the case you're offering is not :)

Hi ! I might be doing something wrong, but I still have an issue with custom errors in async calls. Consider the following:

class CustomErrorType {}
// ...
test('check throws custom error', async () => {            
    async function check() {
        throw new CustomErrorType();
    }
    await expect(check()).rejects.toThrow(CustomErrorType);
});

then the test fails with the following output:

Expected the function to throw an error of type:
"CustomErrorType"
But it didn't throw anything.

So the test fails - whilst it works perfectly when the thrown class is Error. When I try to extend Error with
```javascript
class CustomErrorType extends Error {}
````

then the error is

Expected the function to throw an error of type:
"CustomErrorType"
Instead, it threw:
Error

Any clue on something what is wrong in that sample ? I'm using jest 23.4.2.

@Marchelune When you write class CustomErrorType extends Error {} you haven't actually defined a new class. If you give CustomErrorType a body, even just a constructor that does nothing but call super(), it will work.

In case someone throw anything different from instance Error class and struggle to use advise from that thread (as I did).
toThrow() will check what value thrown is the instance of Error class, and if it is not - throw will not be detected. This wasn't obvious from the docs and common sense.
This will fail, even though it clearly throws:

async function f() {throw 'aa'}
const res = await expect(f()).rejects.toThrow()`

but this will work (not sure if there is a better way):

async function f() {throw 'aa'}
const res = await expect(f()).rejects.toBeTruthy()`

@Karabur typo toBeThruthy should be toBeTruthy

Here's an explicit test that assures it will definitely break if it does NOT throw and matches specific error.

yourCoolAsyncMethod('yeah it is')
    .then(ok => { // Presumes not a Promise<void>
        expect(ok).toBeUndefined();
        done();
    })
    .catch(bad => {
        expect(bad).toBeDefined();
        expect(bad).toMatchInlineSnapshot(
            `Specifically serious error!`
        );

        done();
    });

Hi @SimenB.
If I have this and it's working.

async function check() {
    // do something
}
await expect(check()).rejects.toThrow(InternalServerErrorException);

InternalServerErrorException is thrown with an object....

How can I also assert for properties on the error thrown? For example:

  • exception.message = 'ERROR'
  • exception.status = '500'

Here's an explicit test that assures it will definitely break if it does NOT throw and matches specific error.

yourCoolAsyncMethod('yeah it is')
    .then(ok => {
        expect(ok).toBeUndefined();
        done();
    })
    .catch(bad => {
        expect(bad).toBeDefined();
        expect(bad).toMatchInlineSnapshot(
            `Specifically serious error!`
        );

        done();
    });

Problem about this is that your test will pass if the code never has errors.

So this test would not really offer much value as it's expected results are varied based on whether the code it's testing has an error or not.

Tests should have a negative flow or a positive flow and we should only test what is needed to test. This sort of randomness isn't the greatest.

Hey @David-Tennant 🖐

Thanks for pointing out that. I was working on a Promise<notVoid> method at that time. I realized that I didn't share a common solution that is also valid for Promise<void>. I updated my answer with a comment says I made assumptions. I agree with you about the flow.

Thanks 💨

Howdy @futuredayv 👋

Hey @David-Tennant 🖐

Thanks for pointing out that. I was working on a Promise<notVoid> method at that time. I realized that I didn't share a common solution that is also valid for Promise<void>. I updated my answer with a comment says I made assumptions. I agree with you about the flow.

Thanks 💨

The problem is that your test will still pass if the test does return void. Which should be a bug right?
Calling done() means "My test has passed in a way that is expected and I am ready to move onto my next test"

yourCoolAsyncMethod('yeah it is')
     // -- Lets say your method does not throw, and returns void instead
    .then(ok => { // Presumes not a Promise<void>
        expect(ok).toBeUndefined(); // -- At this point if it was void, this would pass
        done(); // -- Then your test would end and the catch would not be reached
        // -- Now you have unknown behavior passing a test where it's suppose to catch a fail
    })
    .catch(bad => {
        // -- This block does not run because done() is called above
        expect(bad).toBeDefined();
        expect(bad).toMatchInlineSnapshot(
            `Specifically serious error!`
        );

        done();
    });

Not sure what the solution would be for your use case. But I would start by not calling done() in the then or maybe throwing an error in the "then" to make sure the test fails,

yourCoolAsyncMethod('yeah it is')
    .then(ok => throw new Error("This block of code should not be reached")
    .catch(bad => {
        expect(bad).toBeDefined();
        expect(bad).toMatchInlineSnapshot(
            `Specifically serious error!`
        );

        done();
    });

In case someone throw anything different from instance Error class and struggle to use advise from that thread (as I did).
toThrow() will check what value thrown is the instance of Error class, and if it is not - throw will not be detected. This wasn't obvious from the docs and common sense.
This will fail, even though it clearly throws:

async function f() {throw 'aa'}
const res = await expect(f()).rejects.toThrow()`

but this will work (not sure if there is a better way):

async function f() {throw 'aa'}
const res = await expect(f()).rejects.toBeTruthy()`

A slightly better way is to use toBeDefined() instead of toBeTruthy():
async function f() {throw 'aa'} const res = await expect(f()).rejects.toBeTruthy()`

Was this page helpful?
0 / 5 - 0 ratings