tschaub / mock-fs

Configurable mock for the fs module
https://npmjs.org/package/mock-fs
Other
911 stars 86 forks source link

Feature-Request: support/instrument lazy requires #213

Closed osher closed 4 years ago

osher commented 7 years ago

The breaking change of lazy-require is a real game-changer, and for the worst.

My use cases are around data-driven system that load modules dynamically - which is a core concept in many of the system I work on: modules implement strategies, and are loaded dynamically on run-time, instead of loading everything on process startup - which could be a thousand of different strategy files.

Can you think of a way to do that? Maybe a way to support an option to intercepts reads from node_modules, and pass them to the real fs? Or exclude paths in general, regardless to node_modules?

If you can decide on a way you can do that - I can work on it and do the PR if that helps

osher commented 7 years ago

From the docs:

The code below makes it so the fs module is temporarily backed by a mock file system with a few files and directories.

I can also propose to support a mode that instead make backed-by a mock files-system - make some paths on the real file-system shadowed by mocks by a provided pattern-set? we can discuss if the pattern includes or excludes given paths, I'm open to anything and will love to work with you, really

rprieto commented 7 years ago

I agree, took me a while to figure out what was happening. It was a tests for a module that uses micromatch:

mock({
  'file.txt': 'hello'
})
// this is just a regex matcher, it doesn't use the file system at all.... or does it?
micromatch('file.txt', '*.txt')

The problem is that micromatch uses lazy-cache which requires packages on demand. The tests then fails with:

Error: ENOENT, no such file or directory 'node_modules/use/node_modules/define-property'

Unfortunately I have no control over that. I also can't restore() the filesystem before this happens, because the call to micromatch() is part of the function being tested.

Switching to mock-fs@3.x.x fixes the issue. I like the idea of passing-through any calls that reference ./node_modules. Note it's not just reads, it could be fs.stat etc. Another option could be a generic pass-through like:

mock({
  'file.txt': 'hello'
  'node_modules': mock.passthrough()
})
rprieto commented 7 years ago

Update: a workaround for version 4 is to ensure (if possible) that all dynamic modules are loaded before you start mocking the file system. This worked for me:

it('bootstraps micromatch', () => {
  // this will work because we haven't called mock() yet
  // and it loads all the packages we'll need later
  require('micromatch').match('file.txt', '**/**')
})

it('works now', () => {
  mock({
    'file.txt': 'hello'
  })
  micromatch('file.txt', '*.txt')
})
noamokman commented 6 years ago

Similar problem here, Can't console.log in a jest test because it internally requires something. Maybe we can add an option:

mock({
  'file.txt': 'hello',
}, {
     enableLazyLoad: true
});
ronp001 commented 6 years ago

I wrote a small utility that provides a slightly different workaround: it simplifies the copying of selected portions of the real filesystem into the mocked fs.

The downside is that, unlike the workaround by @rprieto, it doesn't import all dependencies automatically. The upside is that it gives you a lot of flexibility in what you copy to the mocked fs (doesn't have to be a package).

For example (in TypeScript):

import * as mockfs from 'mock-fs'
import { MockFSHelper, AbsPath } from "@ronp001/ts-utils"

let simfs = {
  'file.txt': 'hello',
}

// the helper copies contents from the real filesystem into the simfs definition
// must be called before activating mockfs()
const helper = new MockFSHelper(simfs)
helper.addDirContents(new AbsPath('./node_modules/callsites')) 

mockfs(simfs)

To use this you'll need @ronp001/ts-utils as a dependency:

yarn add --dev @ronp001/ts-utils

see here for a real-world example

nonara commented 4 years ago

Just a heads up - I believe #304 should alleviate people's frustrations.

We add the ability to automatically create dir/files from the filesystem, with options recursive and lazyLoad. And as a bonus, there's a function to selectively bypass the mock system.

Have a look at the updated readme entries:

https://github.com/nonara/mock-fs#mapping-real-files--directories https://github.com/nonara/mock-fs#bypassing-the-mock-file-system

Feel free to share your thoughts or questions!