Sean
Sean

Reputation: 5124

Can you write async tests that expect toThrow?

I'm writing an async test that expects the async function to throw like this:

it("expects to have failed", async () => {
  let getBadResults = async () => {
    await failingAsyncTest()
  }
  expect(await getBadResults()).toThrow()
})

But jest is just failing instead of passing the test:

 FAIL  src/failing-test.spec.js
  ● expects to have failed

    Failed: I should fail!

If I rewrite the test to looks like this:

expect(async () => {
  await failingAsyncTest()
}).toThrow()

I get this error instead of a passing test:

expect(function).toThrow(undefined)

Expected the function to throw an error.
But it didn't throw anything.

Upvotes: 484

Views: 195741

Answers (10)

Peter Akinlolu
Peter Akinlolu

Reputation: 33

test("It should test async on failing cloud functions calls", async (done) => {
   failingCloudFunction(params).catch(e => {
    expect(e.message).toBe('Invalid type');
    done()
   })
  });

Upvotes: -3

Gavara.Suneel
Gavara.Suneel

Reputation: 616

You can do like below if you want to use the try/catch method inside the test case.

test("some test case name with success", async () => {
 let response = null;
 let failure = null;
  // Before calling the method, make sure someAsyncFunction should be succeeded
 try {
  response = await someAsyncFunction();
 } catch(err) {
  error = err;
 }
expect(response).toEqual(SOME_MOCK_RESPONSE)
expect(error).toBeNull();
})

test("some test case name with failure", async () => {
 let response = null;
 let error = null;
 // Before calling the method, make sure someAsyncFunction should throw some error by mocking in proper way
 try {
  response = await someAsyncFunction();
 } catch(err) {
  error = err;
 }
expect(response).toBeNull();
expect(error).toEqual(YOUR_MOCK_ERROR)
})

Edit:

As my given solution is not taking the advantage of inbuilt jest tests with the throwing feature, please do follow the other solution suggested by @Lisandro https://stackoverflow.com/a/47887098/8988448

it('should test async errors', async () =>  {        
    await expect(failingAsyncTest())
    .rejects
    .toThrow('I should fail');
});

Upvotes: -1

Dmitri R117
Dmitri R117

Reputation: 2832

If you want to test that an async function does NOT throw:

it('async function does not throw', async () => {
    await expect(hopefullyDoesntThrow()).resolves.not.toThrow();
});

The above test will pass regardless of the value returned, even if undefined.

Keep in mind that if an async function throws an Error, its really coming back as a Promise Rejection in Node, not an error (thats why if you don't have try/catch blocks you will get an UnhandledPromiseRejectionWarning, slightly different than an error). So, like others have said, that is why you use either:

  1. .rejects and .resolves methods, or a
  2. try/catch block within your tests.

Reference: https://jestjs.io/docs/asynchronous#asyncawait

Upvotes: 15

Laode Muhammad Al Fatih
Laode Muhammad Al Fatih

Reputation: 4610

Custom Error Class

The use of rejects.toThrow will not work for you. Instead, you can combine the rejects method with the toBeInstanceOf matcher to match the custom error that has been thrown.

Example

it("should test async errors", async () => {
  await expect(asyncFunctionWithCustomError()).rejects.toBeInstanceOf(
    CustomError
  )
})

Upvotes: 30

Jonas Braga
Jonas Braga

Reputation: 625

To be able to make many tests conditions without having to resolve the promise every time, this will also work:

it('throws an error when it is not possible to create an user', async () => {
        const throwingFunction = () => createUser(createUserPayload)

        // This is what prevents the test to succeed when the promise is resolved and not rejected
        expect.assertions(3)

        await throwingFunction().catch(error => {
            expect(error).toBeInstanceOf(Error)
            expect(error.message).toMatch(new RegExp('Could not create user'))
            expect(error).toMatchObject({
                details: new RegExp('Invalid payload provided'),
            })
        })
    })

Upvotes: 14

Günter Zöchbauer
Günter Zöchbauer

Reputation: 657861

This worked for me

it("expects to have failed", async () => {
  let getBadResults = async () => {
    await failingAsyncTest()
  }
  expect(getBadResults()).reject.toMatch('foo')
  // or in my case
  expect(getBadResults()).reject.toMatchObject({ message: 'foo' })
})

Upvotes: 4

Sumit Bopche
Sumit Bopche

Reputation: 748

await expect(async () => { 
    await someAsyncFunction(someParams); 
}).rejects.toThrowError("Some error message");

We must wrap the code in a function to catch the error. Here we are expecting the Error message thrown from someAsyncFunction should be equal to "Some error message". We can call the exception handler also

await expect(async () => { 
    await someAsyncFunction(someParams); 
}).rejects.toThrowError(new InvalidArgumentError("Some error message"));

Read more https://jestjs.io/docs/expect#tothrowerror

Upvotes: 52

Juan Giacosa
Juan Giacosa

Reputation: 194

I've been testing for Firebase cloud functions and this is what I came up with:

test("It should test async on failing cloud functions calls", async () => {
    await expect(async ()=> {
        await failingCloudFunction(params)
    })
    .rejects
    .toThrow("Invalid type"); // This is the value for my specific error
  });

This is built on top of lisandro's answer.

Upvotes: 7

Lisandro
Lisandro

Reputation: 11711

You can test your async function like this:

it('should test async errors', async () =>  {        
    await expect(failingAsyncTest())
    .rejects
    .toThrow('I should fail');
});

'I should fail' string will match any part of the error thrown.

Upvotes: 944

Stephen
Stephen

Reputation: 1416

I'd like to just add on to this and say that the function you're testing must throw an actual Error object throw new Error(...). Jest does not seem to recognize if you just throw an expression like throw 'An error occurred!'.

Upvotes: 109

Related Questions