Sandro Rey
Sandro Rey

Reputation: 2999

Expect a function to throw an exception in Jest

I have a function (hostelService.book) that returns a promise: return Promise.resolve(response); and I made this test:

const action = async () => {
        await hostelService.book(id);
    };


  await expect(action).rejects.toThrow();

but I have this error:

 Matcher error: received value must be a promise

Upvotes: 59

Views: 93343

Answers (5)

Sohail Ashraf
Sohail Ashraf

Reputation: 10604

Jest is throwing this error Matcher error: received value must be a promise because in expect you are just passing the function reference. Without () - action is just a function reference it will not return anything.

To fix this issue you have to call the function in expect like action() so it will return the promise object.

And the second part, you have to throw error like. Promise.reject(new Error('some error')); in the reject so the tothrow condition can be full filled.

Example:

function hostelService() {
   return Promise.reject(new Error('some error'));
}

const action = async () => {
   await hostelService();
};

it( "Should throw the error", async () => {
   await expect(action()).rejects.toThrow('some error');
});

Hope this will be helpful.

Upvotes: 101

Lucas Bernalte
Lucas Bernalte

Reputation: 1377

It seems you almost have it. I have created a codesandbox with a function that throws an error and a test that passes. You can run it with npm run test in a terminal inside codesandbox. Here's the link:

https://codesandbox.io/s/musing-goodall-ssss4?fontsize=14&hidenavigation=1&theme=dark

But basically the code is this:

const action = async () => {
  throw new Error("error!")
}

describe("func", () => {
  it("should fail", async () => {
    await expect(action()).rejects.toThrow()
  })
})

The difference with the code you showed is the call in action() that was missing, maybe that was the type error. I don't know if that was a mistake or it wasn't just code and you just meant to explain what you wanted. In the future, for better understanding and to help your future "helpers", you should post a snippet of your code (or modified to avoid property issues).

Upvotes: 8

Alexey Kureev
Alexey Kureev

Reputation: 2077

Your test fails because you're awaiting the promise (and therefore, it is no longer a Promise when you call .rejects on it). If you remove await from the last statement, it should do the trick:

const action = async () => {
  await hostelService.book(id);
};

expect(action()).rejects.toThrow();

Here is some rationale behind it. async automatically wraps your return type into a Promise. However, when you call await, it synchronously "unwraps" the value of a given Promise and therefore it can't call .rejects on the result (unless you wrap a Promise into another Promise).

I've composed a small example to illustrate what I mean.

Upvotes: 10

Eduard Jacko
Eduard Jacko

Reputation: 2151

Unfortunately you can't test rejects directly with toThrow(). Rejects it self is an error object so you can test for equality.

test('', () => {
  // dont forget to call the action. 
  await expect(action()).rejects.toEqual(new Error())
});

You can find more workarounds here https://github.com/facebook/jest/issues/1700

Upvotes: 2

Shubham
Shubham

Reputation: 779

Here action is function and you're not calling it from expect. I've also removed await from last line

Try this solution

const action = async () => {
  await hostelService.book(id);
};
expect(action()).rejects.toThrow();

Upvotes: 1

Related Questions