31
class TestObject {
  constructor(value) {
    if (value === null || value === undefined) {
      throw new Error('Expect a value!');
    }
  }
}

describe('test the constructor', () => {
  test('it works', () => {
    expect(() => {
      new TestObject();
    }).toThrow();
  });

  test('not work', () => {
    expect(new TestObject()).toThrow();
  });
});

2 Test cases here, one works and the other not.

The failing message for the not work one is as the following:

● test the constructor › not work

Expect a value!

 at new TestObject (tests/client/utils/aaa.test.js:4:11)
 at Object.<anonymous> (tests/client/utils/aaa.test.js:17:12)
     at Promise (<anonymous>)
     at <anonymous>
 at process._tickCallback (internal/process/next_tick.js:188:7)

Why do I need to wrap that call in a function call, we don't need to wrap when the function just return a plain value, or even a promise, we can use async/await to check that in expect() rather than create a function inside expect().

What happened here?

Albert Gao
  • 3,653
  • 6
  • 40
  • 69

2 Answers2

59

Here

expect(new TestObject()).toThrow();

new TestObject() is evaluated first, then expect(...), then ...toThrow(), in accordance with operator precedence. When new TestObject() throws, anything else doesn't matter.

This is why toThrow expects a function that is supposed to throw:

expect(() => {
  new TestObject();
}).toThrow();

This way it can be wrapped with try..catch internally when being called.

It works similarly in Jasmine toThrow and Chai to.throw assertions as well.

Estus Flask
  • 206,104
  • 70
  • 425
  • 565
0

If you are testing an async function use

expect(async () => {await function()}).rejects.toThrow()

You can see more details - https://jestjs.io/docs/asynchronous#resolves-rejects

Koji D'infinte
  • 1,309
  • 12
  • 20