tschaub / mock-fs

Configurable mock for the fs module

Home Page:https://npmjs.org/package/mock-fs

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Problem using console.log with jest and mock-fs

kristianmandrup opened this issue · comments

When using mockFS in a jest test suite, as soon as I add a console.log anywhere, it breaks with the following:

    ENOENT, no such file or directory '/Users/kristianmandrup/repos/project-maker/package-retriever/node_modules/callsites'

      38 | 
      39 |   it('tests', () => {
    > 40 |     console.log('hello')
      41 |   })
      42 | 
      43 |   it('collects files from each path', async () => {
      
      at Binding.<anonymous> (node_modules/mock-fs/lib/binding.js:1060:13)
      at maybeCallback (node_modules/mock-fs/lib/binding.js:42:17)
      at Binding.Object.<anonymous>.Binding.lstat (node_modules/mock-fs/lib/binding.js:1057:10)
      at Object.it (__tests__/collect/collect-files.test.ts:40:13)

Even if I install the callsites module/package, it still comes out with this strange error!
What could be the cause??

I have started on an overlay option that should address this problem. Until this is ready, a workaround is to call console.log() before mocking the filesystem.

Thanks @tschaub, in the meantime I've tried with a jest mock FS plugin, but it suffered from other issues... So looking forward to use an overlay option with this mature lib :)

I am also experiencing this issue. How is the overlay option coming?

@kristianmandrup if you add the file and directory that it says it can't find to your mock fs then it works for me.

Possible workaround

const fsMock = require('mock-fs')

let logsTemp = []
let logMock

exports.mock = (config) => {
  logMock = jest.spyOn(console, 'log').mockImplementation((...args) => {
    logsTemp.push(args)
  })
  fsMock(config)
}

exports.restore = () => {
  logMock.mockRestore()
  fsMock.restore()
  logsTemp.map(el => console.log(...el))
  logsTemp = []
}

Any update on this issue?

commented

Any updates here?

Nothing from me on this. I have not continued work on the overlay option I mentioned above.

Any updates?

Using

process.stdout.write(message + '\n')

doesn't have this problem.

Wow, this had me stumped for almost a full day. Here's the specific error I got, in case it helps match others' search terms.

ENOENT: no such file or directory, lstat '/home/user/foo/node_modules/@jest/source-map/node_modules'

      at _callsites (../../node_modules/@jest/source-map/build/getCallsite.js:19:39)

I wound up working around this by writing a simplified version of console, so that test writers can continue to habitually use console.log, and avoiding the cognitive load of having to remember where it's safe to use console.log and where it isn't.

mocks/console.js

function format(entry) {
  if (typeof entry === "object") {
    try {
      return JSON.stringify(entry);
    } catch (e) {}
  }

  return entry;
}

function log(...msgs) {
  process.stdout.write(msgs.map(format).join(" ") + "\n");
}

module.exports = {
  log,
  warn: log,
  error: log
};

And "install" it:

global.console = require("./__mocks__/console");

Any updates?

By the look of #142, the overlay option is still a work in progress

For those of you who are using Jest, memfs might be a good alternative for mocking fs. You can create a __mocks__/fs.js file:

const { fs } = require('memfs')
module.exports = fs

then in your tests:

const fs = require('fs')
const myFn = require('../my-fn')

jest.mock('fs')

afterEach(() => {
  fs.reset()
})

describe('myFn', () => {
  it('does something with files', () => {
    fs.fromJSON({
      // files to create
    })
    myFn()
    // assertions
  })
})
commented

@silvenon An even shorter version for mocking fs with memfs (no need for a separate manual mock file):

import fs from 'fs';

jest.mock('fs', () => require('memfs').fs);

it('works', () => {
  const data = fs.readFileSync('foo');
  // ...
});

That will only work for that file, though, so it depends on what you want 😉

Other work around that I have come up with (if like me you don't like random output) is to add the following line before mockFs is called:

console = new Console(process.stdout, process.stderr)

I have the same issue, because I am using lstat in my code.

This is a great solution if you only want to mock a specific folder while having access to the rest of the normal filesystem: streamich/fs-monkey#139 (comment) I haven't had any issues with sourcemaps or logging in my tests since switching to this method