-
-
Notifications
You must be signed in to change notification settings - Fork 6.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Bug]: jest.mock not working with ESM support activated #13135
Comments
Thanks for repo. Easy to fix. Change your test file like this: import { jest } from "@jest/globals";
- import { BrowserWindow } from "electron";
- import { exportedForTests } from "../src/main.cjs";
jest.mock("electron", () => ({
app: {
on: jest.fn(),
whenReady: jest.fn(() => Promise.resolve()),
},
BrowserWindow: jest.fn().mockImplementation(() => ({
loadFile: jest.fn(() => Promise.resolve()),
on: jest.fn(),
})),
}));
+ const { BrowserWindow } = await import("electron");
+ const exportedForTests = await import("../src/main.cjs");
// ... As you suspected – ESM evaluates This pattern worked with CJS, because |
Hi @mrazauskas, thanks for your reply. I've tried using dynamic import but it seems that the The following test resulted in error: import { jest } from '@jest/globals';
jest.mock('electron', () => ({
app: {
on: jest.fn(),
whenReady: jest.fn(() => Promise.resolve()),
},
BrowserWindow: jest.fn().mockImplementation(() => ({
loadFile: jest.fn(() => Promise.resolve()),
on: jest.fn(),
})),
}));
const { BrowserWindow } = await import('electron');
const exportedForTests = await import('../src/main.cjs');
test('Private props exported for unit tests', () => {
expect(exportedForTests).toBeDefined();
});
test('func createWindow()', () => {
const { createWindow } = exportedForTests;
createWindow();
expect(BrowserWindow).toHaveBeenCalledTimes(1);
}); Error:
Also tried using the |
Ah.. The are different solution, but perhaps it would be the best to + import { createRequire } from "node:module";
import { jest } from "@jest/globals";
+ const require = createRequire(import.meta.url);
jest.mock("electron", () => ({
app: {
on: jest.fn(),
whenReady: jest.fn(() => Promise.resolve()),
},
BrowserWindow: jest.fn().mockImplementation(() => ({
loadFile: jest.fn(() => Promise.resolve()),
on: jest.fn(),
})),
}));
- const { BrowserWindow } = await import("electron");
+ const { BrowserWindow } = require("electron");
const exportedForTests = await import("../src/main.cjs");
test("Private props exported for unit tests", () => {
expect(exportedForTests).toBeDefined();
});
test("func createWindow()", () => {
const { createWindow } = exportedForTests;
createWindow();
expect(BrowserWindow).toHaveBeenCalledTimes(1);
}); Also works: const { default: electron } = await import("electron");
// inside `test()`
expect(electron.BrowserWindow).toHaveBeenCalledTimes(1); Or (least elegant for my eye): const { BrowserWindow } = (await import("electron")).default; |
I agree that it’s probably better to import CJS modules by using |
This discussion was helpful, but I can't seem to replicate it in my own ESM project with Node v16.16.0 and jest v28.1.3. // myModule.js
import { execSync } from 'node:child_process';
export default function myFunc() {
console.log(execSync('ls package.json').toString());
} // myModule.test.js
import { createRequire } from "node:module";
import { jest } from '@jest/globals';
const require = createRequire(import.meta.url);
jest.mock('node:child_process');
const { execSync } = require('node:child_process');
const myFunc = (await import('../src/myModule.js')).default;
afterEach(() => jest.resetAllMocks());
test('execSync mock should be working', () => {
execSync('echo');
expect(execSync).toHaveBeenCalledWith('echo')
});
test('myFunc should call mocked execSync', () => {
myFunc();
expect(execSync).toHaveBeenCalledWith('ls package.json');
}); My test fails because the module under test still didn't get the mocked version of
It does seem to work if I use a spy instead, which must be created before importing the module under test: const cp = require('child_process');
const execSpy = jest.spyOn(cp, 'execSync').mockImplementation(() => {});
const myFunc = (await import('../src/myModule.js')).default; |
@iamWing Good idea. I think it would be great to add an example with |
Did you try like this: - import { createRequire } from "node:module";
import { jest } from "@jest/globals";
- const require = createRequire(import.meta.url);
- jest.mock("node:child_process");
+ jest.unstable_mockModule("node:child_process", () => ({
+ execSync: jest.fn(),
+ }));
- const { execSync } = require("node:child_process");
+ const { execSync } = await import("node:child_process");
const myFunc = (await import("../src/myModule.js")).default;
// ... |
@mrazauskas Thank you! That got me moving. I guess I also needed to make sure that if I provided a mock implementation, I manually restored that implementation after // myModule.test.js
import { createRequire } from "node:module";
import { jest } from '@jest/globals';
const require = createRequire(import.meta.url);
jest.unstable_mockModule('node:child_process', () => ({
execSync: jest.fn(() => 'foobar'),
}));
const { execSync } = await import('node:child_process');
const myFunc = (await import('../src/myModule.js')).default;
afterEach(() => {
jest.resetAllMocks();
execSync.mockImplementation(() => 'foobar');
});
test('execSync mock should be working', () => {
execSync('echo');
expect(execSync).toHaveBeenCalledWith('echo')
});
test('myFunc should call mocked execSync', () => {
myFunc();
expect(execSync).toHaveBeenCalledWith('ls package.json');
}); |
@mrazauskas Yeah I think it makes sense to keep the examples in the ECMAScript Modules guide. Docs will need to be changed when the APIs are stabled for ESM support anyway, so no point making extensive amount of documentation at this phase. |
Agreed (; Are you up to opening a PR? |
@mrazauskas sure. Let's see if I can find some time to make it later this week. |
@mildmojo Yes I can confirm I don't understand why do you need to |
I extracted this example from my real test code that modifies the mocks on a per-test level. In this example here, it's not actually necessary. 😛 |
Ah I see. It makes sense then. Just wanna make sure before it goes into the docs. Cheers 🍻 |
Thanks guys we are now finally able to mock our ES modules properly again 🎉 - please allow me to share our usage: const mocked = await mockESModule('someDependency', import.meta);
const myModule = (await import('my-module-using-dep.js')).default;
it('...', () => {
mocked.someFunction.mockImplementationOnce(() => 1337);
...
}); Only "downside" now is to switch from static |
This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 30 days. |
This issue was closed because it has been stalled for 30 days with no activity. Please open a new issue if the issue is still relevant, linking to this one. |
This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. |
Version
28.1.3
Steps to reproduce
npm I
npm run start
to verify Electron app shows up without issuenpm t
to run jest for the errorExpected behavior
Expecting Jest to mock functions
app.on
,app.whenReady
and componentBrowserWindow
fromelectron
, then execute the test cases with the mocked functions.Actual behavior
Encountered
TypeError: Cannot read properties of undefined (reading 'whenReady')
when trying to run the test cases.One possible reason I can think of is the imported module is being executed before
jest.mock('electron', ...)
inmain.spec.js
, hence Node is executing the lineapp.whenReady()
inmain.cjs
with the non-mocked version.Additional context
I originally tested with TypeScript &
ts-jest
with ESM support activated. The error I encountered there is very similar, so that I believe the error is fromjest
instead ofts-jest
. See my comment on #10025 (#10025 (comment))Environment
System: OS: macOS 12.5 CPU: (16) x64 Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz Binaries: Node: 16.16.0 - /usr/local/opt/node@16/bin/node Yarn: 1.22.19 - /usr/local/bin/yarn npm: 8.13.2 - /usr/local/bin/npm npmPackages: jest: ^28.1.3 => 28.1.3
The text was updated successfully, but these errors were encountered: