So I have been struggling with how to test dynamic imports generally and in this case, especially in jest, I have been reading a lot on the internet but did not find anything concrete, so I thought about bringing the question up to centralize a decent solution.
I have the following methods inside of a class
class MyClass {
successMethod() { ... }
errorMethod() { ... }
myMethod() {
return import('./myFile.js')
.then(() => this.successMethod())
.catch(() => this.errorMethod());
}
}
My question is:
How do you mock both Success and Failing promise cases for this dynamic import using Jest to make sure each method (successMethod
and errorMethod
) are called when resolving or failing respectively?.
I found jest.doMock
helps for mocking the resolved case but did not find a way to make the dynamic import fail by mocking it so the catch
case is uncovered.
Note: this is not a react application, this is a Vanilla JS project.
So I have been struggling with how to test dynamic imports generally and in this case, especially in jest, I have been reading a lot on the internet but did not find anything concrete, so I thought about bringing the question up to centralize a decent solution.
I have the following methods inside of a class
class MyClass {
successMethod() { ... }
errorMethod() { ... }
myMethod() {
return import('./myFile.js')
.then(() => this.successMethod())
.catch(() => this.errorMethod());
}
}
My question is:
How do you mock both Success and Failing promise cases for this dynamic import using Jest to make sure each method (successMethod
and errorMethod
) are called when resolving or failing respectively?.
I found jest.doMock
helps for mocking the resolved case but did not find a way to make the dynamic import fail by mocking it so the catch
case is uncovered.
Note: this is not a react application, this is a Vanilla JS project.
Share Improve this question edited Sep 25, 2020 at 0:48 Enmanuel Duran asked Sep 14, 2020 at 22:58 Enmanuel DuranEnmanuel Duran 5,1183 gold badges19 silver badges32 bronze badges 4-
1
what about
jest.mock('./myFile.js', () => Promise.reject(new Error('Forcing async import error')));
? – Dipen Shah Commented Sep 17, 2020 at 16:55 -
@DipenShah This will result in successful import with
default
export being promise object. – Estus Flask Commented Sep 22, 2020 at 17:51 - @EstusFlask yes and that should help you test fail scenario, right? Could you please share codesandbox/stablitz project, is should work afaik. – Dipen Shah Commented Sep 22, 2020 at 17:52
-
@DipenShah jest.mock return is treated as CommonJS export which is translated to ESM
default
export. It will triggerthen
callback and notcatch
, becauseimport
returns a promise of ES module export object, which in this case is{ default: Promise.reject(...) }
. – Estus Flask Commented Sep 22, 2020 at 17:56
2 Answers
Reset to default 10 +50Prototype methods can be spied or mocked on either MyClass.prototype
or class instance. Module mocks and spy functions should be restored for each test in order for them to not affect each other.
let myClass;
beforeEach(() => {
jest.resetModules();
jest.restoreAllMocks();
myClass = new MyClass();
jest.spyOn(myClass, 'successMethod');
jest.spyOn(myClass, 'errorMethod');
});
jest.doMock
requires to import all affected modules after it was called. In order for dynamic import
to result in rejected promise, myFile
module should throw an error when evaluated. Since dynamic import
returns a promise, tests should be asynchronous.
it('...', async () => {
jest.mock('./myFile.js', () => 'value');
await expect(myClass.myMethod()).resolves.toEqual(/* return value from successMethod */);
expect(myClass.successMethod).toBeCalled();
expect(myClass.errorMethod).not.toBeCalled();
});
it('...', async () => {
jest.mock('./myFile.js', () => { throw new Error() });
await expect(myClass.myMethod()).rejects.toThrow(/* error message from errorMethod */);
expect(myClass.successMethod).not.toBeCalled();
expect(myClass.errorMethod).toBeCalled();
});
maybe something like
beforeEach(() => {
jest.resetModules();
});
beforeAll(() => {
MyClass.mockImplementation(() => {
return {
successMethod: () => {
console.log("succees");
},
errorMethod: () => {
console.log("error");
}
};
});
});
test("should fail", () => {
jest.doMock("./myFile.js", () => {
return jest.fn(() => {
throw new Error("Parameter is not a number!");
});
});
const kls = MyClass();
kls.myMethod();
expect(kls.errorMethod).toHaveBeenCalledTimes(1);
expect(kls.successMethod).toHaveBeenCalledTimes(0);
});
test("should pass", () => {
jest.doMock("./myFile.js", () => {
return jest.fn(() => 1);
});
const kls = MyClass();
kls.myMethod();
expect(kls.errorMethod).toHaveBeenCalledTimes(0);
expect(kls.successMethod).toHaveBeenCalledTimes(1);
});