Last active
September 28, 2023 12:50
-
-
Save booya2nd/dcaa1775fd4c06cd79610e3feea6362c to your computer and use it in GitHub Desktop.
mockESModule workaround for JEST 29.x
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as path from 'path'; | |
import * as url from 'url'; | |
function forEachDeep(obj, cb, options = { depth: 6 }) { | |
(function walk(value, property = undefined, parent = null, objPath = []) { | |
return value && typeof value === 'object' && objPath.length <= options.depth | |
? Object.entries(value).forEach(([key, val]) => | |
walk(val, key, value, [...objPath, key]) | |
) | |
: cb([property, value], parent, objPath); | |
})(obj); | |
} | |
const NOOP = x => x; | |
export default async function mockESModule( | |
moduleSpecifier, | |
importMeta, | |
factory = NOOP | |
) { | |
let modulePath = moduleSpecifier; | |
if (moduleSpecifier.startsWith('.')) { | |
const metaPath = url.fileURLToPath(new URL('./', importMeta.url)); | |
modulePath = path.join(metaPath, moduleSpecifier); | |
} | |
const { jest } = importMeta; | |
const module = await import(modulePath); | |
const moduleCopy = { ...module }; | |
forEachDeep(moduleCopy, ([prop, value], obj) => { | |
if (typeof value === 'function') { | |
obj[prop] = jest.fn(value); | |
// re-adding stinky dynamic custom properties which jest.fn() may has removed | |
Object.assign(obj[prop], value); | |
} | |
}); | |
const moduleMock = factory(moduleCopy); | |
jest.unstable_mockModule(moduleSpecifier, () => moduleMock); | |
return moduleMock; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import someMagic from './some-magic.js'; | |
export default function(){ | |
return someMagic() ? 'magic' : 'no magic'; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { describe, it, jest } from '@jest/globals'; | |
import mockESModule from './mock-esmodule.js'; | |
// this will mock all functions exported from some-magic.js | |
const mockMagic = (await mockESModule('./some-magic.js', import.meta)).default; | |
// use import() in order to apply mocked version of dependencies | |
const myModule = (await import('./my-module.js')).default; | |
describe('modulename', () => { | |
it('invokes the thing', () => { | |
const result = myModule(); | |
expect(result).toBe('magic'); | |
}); | |
it('throws if magic is falsy', () => { | |
mockMagic.mockImplementationOnce(() => false); | |
expect(myModule).toBe('no magic'); | |
expect(mockMagic).toHaveBeenCalled(); | |
}) | |
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export default function someMagic(){ return true } |
Thanks for sharing, @booya2nd, this is very helpful! Would you be able to specify the license of this code?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Key components:
Mock via
jest.unstable_mockModule()
beforeimport()
- you have to use dynamicimport()
over staticimport
https://github.com/facebook/jest/blob/main/docs/ECMAScriptModules.md#module-mocking-in-esm