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.
Steps to reproduce the behavior:
First, get a Jest environment ready, I've used
[email protected] & [email protected] and [email protected] & [email protected], 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
I expect toThrow
and toThrowError
to work consistently in both Typescript and Babel environment.
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.
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 [email protected] in the other repository.
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);
});
Thanks for the workaround, I also made one for async/await
class CustomException {
message: string
constructor(message: string) {
this.message = message
}
}
class SomethingThatThrows {
async throwIt() {
throw new CustomException('hello')
}
}
const thrower = new SomethingThatThrows()
// In your jest async test
await expect(thrower.throwIt()).rejects.toBeInstanceOf(CustomException)
How do you transpile the TS? Would you mind putting together a repository (or code sandbox) showing the error you get?
Here is a codesandbox reproducing the problem.
https://codesandbox.io/embed/crazy-ptolemy-odtff
class MyError extends Error {}
it("should throw MyError", () => {
function errorProneFunc() {
throw new MyError("my error subclass");
}
expect(errorProneFunc).toThrow(MyError); // fails
});
I encountered this error when using ts-jest
on a bare jest project.
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!
});
Thanks,
This is indeed a TS limitation as you found out. This issue can be closed.
Most helpful comment
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: