question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Asserting against a custom error class using .toThrow and .toThrowError never work in Typescript

See original GitHub issue

🐛 Bug Report

I’m reporting a potential bug with using .toThrow and .toThrowError against custom error classes that only happens in Typescript environment. Both assertions fail to validate custom error classes unlike in the Babel environment. Please see the #To Reproduce section for more detail.

To Reproduce

Steps to reproduce the behavior:

First, get a Jest environment ready, I’ve used

jest@20.0.4 & typescript@2.4.0 and jest@24.7.1 & typescript@3.4.1, and I was able to reproduce the same bug in both environments.

Typescript code:

class BaseError extends Error {
  constructor(message: string) {
    super();
    this.message = message;
    this.name = 'Error';
  }
}

class ValidationError extends BaseError {}
class StrangeError extends BaseError {}

describe('exceptions', () => {
  it('should throw ValidationError', () => {
    function errorProneFunc () {
      throw new ValidationError('zzz');
    }
    expect(errorProneFunc).toThrow(ValidationError); // fails
  });
});

I get below error messages

● exceptions › should throw ValidationError

    expect(function).toThrow(type)
    
    Expected the function to throw an error of type:
      "ValidationError"
    Instead, it threw:
      zzz      
          at ValidationError.BaseError [as constructor] (src/__tests__/HelloWorld.test.ts:3:5)
          at new ValidationError (src/__tests__/HelloWorld.test.ts:25:42)
          at errorProneFunc (src/__tests__/HelloWorld.test.ts:15:13)
          at Object.<anonymous> (src/__tests__/HelloWorld.test.ts:17:28)
              at new Promise (<anonymous>)

However, on a standard Babel setup with the exact same code (except for the type declarations), I cannot reproduce the bug at all. Please check out

https://repl.it/repls/FrankHarshVaporware

and run add_test.js

Expected behavior

I expect toThrow and toThrowError to work consistently in both Typescript and Babel environment.

Link to repl or repo (highly encouraged)

As a result, I cannot write good unit tests to assert based on Error type and have to assert based on error messages, which breaks all the time. Please check my project https://github.com/machinelearnjs/machinelearnjs/tree/master/test

Issues without a reproduction link are likely to stall.

Run npx envinfo --preset jest

System:
  Linux & Mac
Binaries:
    Node: 10.15.1 - /usr/local/bin/node
    Yarn: 1.13.0 - /usr/local/bin/yarn
    npm: 6.9.0 - /usr/local/bin/npm
npmPackages:
    jest: ^20.0.4 => 20.0.4 

Also I’m using jest@24.7.1 in the other repository.

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:7
  • Comments:8

github_iconTop GitHub Comments

23reactions
ayliaocommented, Oct 9, 2019

I think this may be a TypeScript limitation rather than a Jest issue. According to this changelog, extending built-ins in TypeScript comes with its caveats.

As a workaround, you can do what they suggest in the changelog and explicitly set the prototype of your custom error. So your example would be:

class MyError extends Error {
  constructor(m: string) {
    super(m);

    // Set the prototype explicitly.
    Object.setPrototypeOf(this, MyError.prototype);
  }
}

it("should throw MyError", () => {
  function errorProneFunc() {
    throw new MyError("my error subclass");
  }

  expect(errorProneFunc).toThrow(MyError); // passes, yay!
});
6reactions
JasonShincommented, Apr 6, 2019

As a workaround in Typescript, I have to declare the custom error classes like

export const ValidationError = function (message) {
  Error.captureStackTrace(this, this.constructor);
  this.name = this.constructor.name;
  this.message = message;
};

and assert like

 try {
      lr.fit('abc' as any, y1);
} catch (e) {
      expect(e).toBeInstanceOf(ValidationError);
}

Or if you are using promises

lr.fit(...).catch((e) => {
  expect(e).toBeInstanceOf(ValidationError);
});
Read more comments on GitHub >

github_iconTop Results From Across the Web

asserting against thrown error objects in jest - Stack Overflow
As Dez has suggested the toThrowError function will not work if you do not throw an instance of a javascript Error object.
Read more >
How to Fix instanceof Not Working For Custom Errors in ...
In JavaScript, you can create custom errors by extending the built-in Error object (ever since ES 2015). Copy. class DatabaseError extends ...
Read more >
Expect · Jest
When an assertion fails, the error message should give as much signal as necessary to the user so they can resolve their issue...
Read more >
Expect a function to throw an exception in Jest - eloquent code
Suppose we want to test the following function using Node.js and assert that it indeed throws an error: func.js:
Read more >
API Reference | Vitest
test.skip , test.only , and test.todo works with concurrent tests. ... { test('composed of non-numbers to throw error', () => { expect(() ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found