Expect a function to throw an exception in Jest

31,373

Solution 1

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.

Solution 2

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.

Solution 3

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).

Solution 4

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

Share:
31,373
Sandro Rey
Author by

Sandro Rey

Bendiciones y unos Nullpointers. y si no sabes la respuesta a mis preguntas que te den por el xxxx dos veces.

Updated on January 22, 2020

Comments

  • Sandro Rey
    Sandro Rey over 4 years

    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
    
    • jonrsharpe
      jonrsharpe over 4 years
      It's only toThrow that calls the supplied value. rejects requires it to already be a promise, not a function that returns one. I don't think .rejects.toThrow() makes sense at all; you probably want expect(hostelService.book(id)).rejects.toEqual(someError). See jestjs.io/docs/en/tutorial-async.html#rejects.
  • JHH
    JHH almost 3 years
    Your last comment is incorrect. You should not remove await from the expect(...).rejects.toThrow('some message'); statement. It may or may not work depending on if it's the last statement of the test etc, but what you're essentially doing is finishing the test with async code still executing, which might mean you'd be calling tear down code mid-test. Always await asynchronous calls in your tests.