jest not matching custom error type when using dynamic import

Viewed 736

I have this custom error defined (file name: 'errors.ts'):

export class CustomError extends Error {
    constructor(message?: string) {
        super(message);
        Object.setPrototypeOf(this, Error.prototype);
        this.name = this.constructor.name;
    }
}

And I have this module using that error (file name: 'main.ts'):

import { CustomError } from './errors';

let called = false;
export default {
    throwAfterFirst: (): void => {
        if (called) throw new CustomError();
        called = true;
    },
};

I want to check with jest using dynamic imports so I could call jest.restModule() to reset called variable. This is the test file:

import { CustomError } from './errors';

let main: typeof import('./main').default;
const load = async (): Promise<Main> => {
    jest.resetModules();
    main = (await import('./main')).default;
};

describe('Main', () => {
    beforeEach(async () => {
        await load();
    });

    it('should throw on second time', () => {
        manager.throwAfterFirst();
        expect(() => manager.throwAfterFirst()).toThrowError(CustomError);
    });

    it('should still throw on second time', () => {
        manager.throwAfterFirst();
        expect(() => manager.throwAfterFirst()).toThrowError(CustomError);
    });
});

Now this is half working since the module is indeed reset before each test but the problem is toThrowError isn't matched correctly and I get the following error for both tests on the second line:

expect(received).toThrowError(expected)

Expected constructor: CustomError
Received constructor: CustomError
.
.
.

This weird bug does not occur with regular errors (e.g. replace CustomError with TypeError).
Also, CustomError can be matched successfully without dynamic importing. For example, the following test works fine:

import { CustomError } from './errors';
import main from './main';

describe('Main', () => {
    it('should throw on second time', () => {
        manager.throwAfterFirst();
        expect(() => manager.throwAfterFirst()).toThrowError(CustomError);
    });
});

Since I'm using dynamic import CustomError is not recognized correctly (perhaps not the same prototype?). What am I missing here, and how can I fix the test?
(I still want to check specific error and not use toThrow which match any error. BTW toThrow works on this scenario).

1 Answers

Workaround I found is this:

let main: typeof import('./main').default;
let Errors: typeof import('./errors');
const load = async (): Promise<void> => {
    jest.resetModules();
    main = (await import('./main')).default;
    Errors = await import('./errors');
};

describe('Main', () => {
    beforeEach(async () => {
        await load();
    });

    it('should throw on second time', () => {
        manager.throwAfterFirst();
        expect(() => manager.throwAfterFirst()).toThrowError(Errors.CustomError);
    });

    it('should still throw on second time', () => {
        manager.throwAfterFirst();
        expect(() => manager.throwAfterFirst()).toThrowError(Errors.CustomError);
    });
});
Related