-
Notifications
You must be signed in to change notification settings - Fork 18
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
Question / Bug - Unable to mock commonjs dependencies #312
Comments
Adding This is un-expected because the I've tried various things but with no success. Tried specifying The full tests from a fresh cloned esmock are passing here, so the module loader hooks are working normally for many tests there. Not sure what the solution is but I will keep thinking about it. Thanks for opening the issue and creating the reproduction sample. |
I added the test inside the existing esmock test files and it fails there. I removed the contents of changlog-parser's index.js to be sure that the readline package is not interfering with the terminal output in some way. Added this inside the "load" hook function and when changelog-parser is loaded, the first the console.log appears in the process output, but the subsequent calls are not seen... console.log('PASS1', { url })
console.log('PASS2', { url }) // not logged
console.log('PASS3', { url }) // not logged My intuition is a simplified test could probably be made to demonstrate this as a node.js bug, but I don't have bandwidth at the moment to make one. Perhaps, under certain conditions, node.js wrongly continues the stack before module-loader hooks have returned values. If you don't mind using unstable newer releases of node, you could try this new module mocking functionality nodejs/node#52848 |
Thank you for your quick reply, I tried using the native |
Thanks for the update I hope a solution will appear |
readline imports fs inside of an iffe and that may be what causes it to early-load fs before it is processed by the module-loader (function () {
var fs = require('fs')
// ...
}()); |
The very same use-case seems to be happening with graceful-fs library. I also cannot mock the import { test, mock } from 'node:test';
import assert from 'node:assert';
import esmock from 'esmock';
test('graceful-fs', async () => {
const content = 'content';
const fs = await esmock('graceful-fs', {}, {
fs: {
readFileSync: mock.fn(() => content),
},
});
assert.equal(fs.readFileSync(), content);
}); Not sure if patching |
I noticed graceful-fs does not require/import Maybe I hold a misunderstanding about the way the moduleLoader works or maybe this is a bug in the nodejs runtime... the next step should be to make a smaller test isolating the inability of the 'resolve' and 'load' hooks to indertict require usages. There are many esmock tests and it is surprising they are missing whatever is happening here. |
related nodejs/node#55307 |
This single file mocks the repro.mjsimport { register } from 'node:module';
import assert from 'node:assert';
import fs from 'node:fs'
function log (...args) {
return fs.writeSync(1, JSON.stringify(args, null, ' ').slice(2, -1))
}
async function resolve(referrer, context, next) {
const result = await next(referrer);
const url = new URL(result.url)
if (result.url === 'node:fs') {
result.url = import.meta.url + '#-#' + result.url
}
return result
}
async function load(url, context, next) {
log('load: ' + url.slice(0, 60))
if (url.endsWith('#-#node:fs')) {
log('second: node:fs is imported from dummy url import.meta.url', {
url: url.slice(0, 60)
})
return {
shortCircuit: true,
format: 'commonjs',
responseURL: url,
source: `
module.exports = {
readFileSync: () => 'test'
}`
}
}
if (url.endsWith('file.js')) {
log('first: parent that includes node:fs is returned as cjs', {
url: url.slice(0, 60)
})
return {
shortCircuit: true,
format: 'commonjs',
responseURL: url,
source: `
const fs = require('node:fs')
module.exports = p => fs.readFileSync(p, 'utf8')`
}
}
if (url.endsWith('#-#node:fs')) {
log('second: node:fs is imported from dummy url import.meta.url', {
url: url.slice(0, 60)
})
return {
shortCircuit: true,
format: 'commonjs',
responseURL: url,
source: `
module.exports = {
readFileSync: () => 'test'
}`
}
}
return next(url, context);
}
register([
`data:text/javascript,`,
`import fs from 'node:fs';`,
`${encodeURIComponent(log)};`,
`export ${encodeURIComponent(resolve)};`,
`export ${encodeURIComponent(load)}`
].join(''));
const testread = (await import('./file.js')).default
assert.strictEqual(testread('testpath'), 'test') |
For anyone who encounters this issue, you should be fine if your tests apply mocks to ESM targets Per the description, esmock provides: "ESM import and globals mocking for unit tests" but sample tests associated with this issue attempt to mock to import trees that are entirely commonjs. Many subtle time-consuming adjustments are needed for esmock to possibly handle this one and there's no certainty they would be succesful.
|
Hi,
thank you for this great library, thanks to this, we were able to actually use native node test runner in some of our projects!
I ran into an issue, but maybe it is expected behaviour. When I am trying to mock a dependency of a dependency, it does not really work. Maybe the issue is that both dependencies are commonjs modules?
I prepared a reproduction repository - https://github.com/Filipoliko/esmock-bug - but the use-case is quite straightforward. Ultimately, I am trying to mock
fs
module globally (even within dependencies likechangelog-parser
), but I am failing to do so. Being desperate, I tried to mockline-reader
module, which is the one using thefs
module in my case, but this failed also. This is a simplified example, where I was unable to mock the 3rd party dependency of a dependency.Code
Output (Node 22.9.0)
The result is, that there is no mocked
fs
, norline-reader
and the test really tries to openfake
file, which fails.I am trying to use memfs as a replacement for native node
fs
, since we are trying to create some sort of an integration test.The text was updated successfully, but these errors were encountered: