Search code examples
unit-testingexceptionjestjsnestjs

Expecting specific error when writing unit tests in jest


I use nestjs (6.5.0) and jest (24.8) and have a method that throws an error:

  public async doSomething(): Promise<{ data: string, error?: string }> {
    throw new BadRequestException({ data: '', error: 'foo' });
  }

How can I write a unit test that checks that we get the expected exception with the expected data? The obvious solution is:

it('test', async () => {
  expect(await userController.doSomething())
    .rejects.toThrowError(new BadRequestException({ data: '', error: 'foo'});
});

but that doesn't work because new BadRequestException() creates an object with a different call stack. How can I test this?


Solution

  • Answering my own question:

    With a custom matcher (see below) the test can be written as:

    it('test', async () => {
      await expect(userController.doSomething()).rejects.toContainException(
        new BadRequestException({ data: '', error: 'foo' }),
      );
    });
    

    Custom matcher:

    import { HttpException } from '@nestjs/common';
    
    // ensure this is parsed as a module.
    export {};
    
    // https://stackoverflow.com/questions/43667085/extending-third-party-module-that-is-globally-exposed
    
    declare global {
      namespace jest {
        interface Matchers<R> {
          toContainException: (expected: R | any) => {};
        }
      }
    }
    
    // this will extend the expect with a custom matcher
    expect.extend({
      toContainException<T extends HttpException>(received: T, expected: T) {
        const success =
          this.equals(received.message, expected.message) &&
          this.equals(received.getStatus(), expected.getStatus());
    
        const not = success ? ' not' : '';
        return {
          message: () =>
            `expected Exception ${received.name}${not} to be ${expected.name}` +
            '\n\n' +
            `Expected: ${this.utils.printExpected(expected.message)}, ` +
            `status: ${this.utils.printExpected(expected.getStatus())} \n` +
            `Received: ${this.utils.printReceived(received.message)}, ` +
            `status: ${this.utils.printReceived(received.getStatus())}`,
          pass: success,
        };
      },
    });