The tl;dr is:
1) How can I have Jest use the native require
function to load all modules in my tests anywhere.
2) Where / how would I go about modifying (ie replacing with the esm loader) https://github.com/standard-things/esm the require function in one place, before any tests run, so all tests will use the modified require.
I'd like to use the esm-loader with my Jest test files. In order to do so, I need to patch the require function globally, before any test code runs, with something like
require = require("@std/esm")(module, { esm: "js", cjs: true });
How do I tell Jest to execute that code before anything else is touched or requested?
I tried pointing both setupTestFrameworkScriptFile
and an setupFiles
array entry to a file with that in it, but neither worked (though I did confirm that both ran).
Alternatively, I'm firing off these tests with an npm script
"scripts": {
"test": "jest"
}
Is there some CLI magic whereby I can just load a module and then run jest
?
Edit - the testEnvironment
and resolver
options make me wonder if this is ever even using the actual Node require
function to load modules, or instead using its own module loader. If so I wonder if this is even possible.
If beforeEach is inside a describe block, it runs for each test in the describe block. Using the same example where we have a database with test data, if your tests do not need the test data reset for each test, you can use beforeAll to run some code once, before any tests run.
beforeEach(fn, timeout) Runs a function before each of the tests in this file runs. If the function returns a promise or is a generator, Jest waits for that promise to resolve before running the test. Optionally, you can provide a timeout (in milliseconds) for specifying how long to wait before aborting.
rootDir [string] Default: The root of the directory containing the package.json or the pwd if no package.json is found. Please provide your exact Jest configuration and mention your Jest, node, yarn/npm version and operating system.
So this one was a bit tough to get working. The solution is quite simple but it took me a while to get it working. The problem is that whenever you use any module in jest
They are all loaded in below way
({"Object.":function(module,exports,require,__dirname,__filename,global,jest){/*Module code inside*/ }});
If you have a look at node_modules/jest-runtime/build/index.js:495:510
const dirname = (_path || _load_path()).default.dirname(filename); localModule.children = []; localModule.parent = mockParentModule; localModule.paths = this._resolver.getModulePaths(dirname); localModule.require = this._createRequireImplementation(filename, options); const transformedFile = this._scriptTransformer.transform( filename, { collectCoverage: this._coverageOptions.collectCoverage, collectCoverageFrom: this._coverageOptions.collectCoverageFrom, collectCoverageOnlyFrom: this._coverageOptions.collectCoverageOnlyFrom, isInternalModule, mapCoverage: this._coverageOptions.mapCoverage }, this._cacheFS[filename]);
this._createRequireImplementation(filename, options);
gives every module a custom require object. So you as such don't get the native require function at all, anywhere. Once jest has started every module loaded from then on will have jest's custom require
function.
When we load a module, the requireModule
methods from the jest-runtime
gets called. Below is an excerpt from the same
moduleRegistry[modulePath] = localModule; if ((_path || _load_path()).default.extname(modulePath) === '.json') { localModule.exports = this._environment.global.JSON.parse( (0, (_stripBom || _load_stripBom()).default)((_gracefulFs || _load_gracefulFs()).default.readFileSync(modulePath, 'utf8'))); } else if ((_path || _load_path()).default.extname(modulePath) === '.node') { // $FlowFixMe localModule.exports = require(modulePath); } else { this._execModule(localModule, options); }
As you can see if the extension of the file is .node
it loads the module directly, else it calls the _execModule
. This function is the same code that I posted earlier which does the code transformation
const isInternalModule = !!(options && options.isInternalModule); const filename = localModule.filename; const lastExecutingModulePath = this._currentlyExecutingModulePath; this._currentlyExecutingModulePath = filename; const origCurrExecutingManualMock = this._isCurrentlyExecutingManualMock; this._isCurrentlyExecutingManualMock = filename; const dirname = (_path || _load_path()).default.dirname(filename); localModule.children = []; localModule.parent = mockParentModule; localModule.paths = this._resolver.getModulePaths(dirname); localModule.require = this._createRequireImplementation(filename, options);
Now when we want to modify require
function for our test, we need _execModule
to export our code directly. So the code should be similar to loading of a .node
modules
} else if ((_path || _load_path()).default.extname(modulePath) === '.mjs') { // $FlowFixMe require = require("@std/esm")(localModule); localModule.exports = require(modulePath); } else {
But doing that would mean patching the code, which we want to avoid. So what we do instead is avoid using the jest command directly, and create our own jestload.js
and running that. The code for loading jest is simple
#!/usr/bin/env node /** * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ cli = require('jest/bin/jest');
Now we want to modify the _execModule
before the cli loads. So we add below code
const jestRuntime = require("jest-runtime"); oldexecModule = jestRuntime.prototype._execModule; jestRuntime.prototype._execModule = function (localModule, options) { if (localModule.id.indexOf(".mjs") > 0) { localModule.exports = require("@std/esm")(localModule)(localModule.id); return localModule; } return oldexecModule.apply(this, [localModule, options]); }; cli = require('jest/bin/jest');
Now time for a test
//__test__/sum.test.js sum = require('../sum.mjs').sum; test('adds 1 + 2 to equal 3', () => { expect(sum(1, 2)).toBe(3); }); test('adds 2 + 3 to equal 5', () => { expect(sum(3, 2)).toBe(5); });
And a sum.mjs
file
export function sum (x, y) { return x + y }
Now we run the test
The solution is available on below repo
https://github.com/tarunlalwani/jest-overriding-require-function-stackoverflow
You can clone and test the solution by running npm test
.
setupFiles
worked for me. Add this in package.json:
"jest": {
"setupFiles": ["./my_file.js"]
},
https://jestjs.io/docs/en/configuration.html#setupfiles-array
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With