Skip to content
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

Dynamic imports are unexpectedly coupled between tests #340

Open
mtmacdonald opened this issue Oct 28, 2021 · 2 comments
Open

Dynamic imports are unexpectedly coupled between tests #340

mtmacdonald opened this issue Oct 28, 2021 · 2 comments

Comments

@mtmacdonald
Copy link

mtmacdonald commented Oct 28, 2021

I'm trying to use mock-fs to unit test code which uses ES6 dynamic imports.

There seems to an unexpected coupling between tests when I'm using dynamic imports, even when I call restore() after each test. It appears as though fs.readFile() behaves as expected between tests (no coupling), but await import() has coupling (it returns the result from the previous test).

I've created a minimal Jest test case that reproduces the issue. The tests pass individually, but not when run together. I notice that if I change the directory value so it's different between each test, then they pass together.

Can you help me understand why this doesn't work, whether it's a bug, and what I should do here?

import path from 'path';
import { promises as fs } from 'fs';
import mockFs from 'mock-fs';

const fsMockModules = {
  node_modules: mockFs.load(path.resolve(__dirname, '../node_modules')),
};

describe('Reproduce dynamic import coupling between tests', () => {
  afterEach(() => {
    mockFs.restore();
  });
  it('first test', async () => {
    const directory = 'some/path';
    mockFs({
      ...fsMockModules,
      [directory]: {
        'index.js': ``,
      },
    });
    await import(path.resolve(`${directory}/index.js`));
    //not testing anything here, just illustrating the coupling for next test
  });
  it('second tests works in isolation but not together with first test', async () => {
    const directory = 'some/path';
    mockFs({
      ...fsMockModules,
      [directory]: {
        'index.js': `export {default as migrator} from './migrator.js';`,
        'migrator.js':
          'export default (payload) => ({...payload, xyz: 123});',
      },
    });
    const indexFile = await fs.readFile(`${directory}/index.js`, 'utf-8');
    expect(indexFile.includes('export {default as migrator}')).toBe(true);
    const migrations = await import(path.resolve(`${directory}/index.js`));
    expect(typeof migrations.migrator).toBe('function');
  });
});
@3cp
Copy link
Collaborator

3cp commented Nov 6, 2021

When nodejs calls import() on the same file twice, it will reuse loaded module from 1st call for the 2nd call, same as you do require() twice in CommonJS code before.

Here is the approve, inside a folder, create two files:

  1. package.json
{ "type": "module" }
  1. foo.js
console.log("loading foo");
export default "FOOooOO";

Then in nodejs REPL command line, create a function test() to import foo and print out result.
You can see the first import() triggered the console log "loading foo" in the foo.js, but second import() didn't log it again, means nodejs didn't read foo.js again.

> node
Welcome to Node.js v14.17.0.
Type ".help" for more information.
> async function test() {
...   const foo = await import('./foo.js');
...   console.log('foo', foo);
... }
undefined

> test()
Promise { <pending> }
> loading foo
foo [Module: null prototype] { default: 'FOOooOO' }

> test()
Promise { <pending> }
> foo [Module: null prototype] { default: 'FOOooOO' }

@npmrun
Copy link

npmrun commented Apr 12, 2023

@3cp
Do you know how to delete import() cache?
I am using this temporary solution:

import("./foo.js?"+new Date().getTime())

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants