How do I stub node.js built-in fs during testing?

前端 未结 11 2106
暖寄归人
暖寄归人 2021-02-05 02:21

I want to stub node.js built-ins like fs so that I don\'t actually make any system level file calls. The only thing I can think to do is to pass in fs

相关标签:
11条回答
  • 2021-02-05 02:34

    An alternative (although I think Noah's suggestion of rewire is better):

    Write a wrapper around require, named requireStubbable or so. Put this in a module which you configure once, in test setup code. Because node caches result of require, whenever you require the requireStubbable module again, you'd get the same configured function. You could configure it so that any number of modules would be stubbed, all others would be passed on unchanged.

    Any module which you'd want to support passing in stubs need to use the requireStubbable function instead of regular require though. The rewire module does not have that drawback, and instead gives control to the calling code.

    Added April 26

    I've never realized, but since the object (or more precisely: object reference) returned by require("fs") is cached, you could simply do:

    const fs = require("fs")
    fs.readFile = function (filename, cb) {
      cb(null, new Buffer("fake contents"));
    };
    // etc
    

    When you include this code anywhere, fs.readFile will be pointing to the above function everywhere. This works for stubbing out any module that is a mere collection of functions (like most built-in modules). The cases for which it doesn't work if the module returns a sole function. For this, something like rewire would be necessary.

    0 讨论(0)
  • 2021-02-05 02:38
    var fs = require('./myStubFs');
    

    Would seem to be a big improvement. Whatever solution you find will probably involve writing your own stub functions. The ideal solution would be lower level so you don't have to touch all the files you want doing this, e.g. perhaps you want 3rd party libraries stubbed out as well.

    0 讨论(0)
  • 2021-02-05 02:41

    Rewire and other stubbing solutions are good if the module under test is the one making calls to fs itself. However, if the module under test uses a library which uses fs underneath, rewire and other stubbing solution get hairy pretty quickly.

    There is a better solution now: mock-fs

    The mock-fs module allows Node's built-in fs module to be backed temporarily by an in-memory, mock file system. This lets you run tests against a set of mock files and directories instead of lugging around a bunch of test fixtures.

    Example (shamelessly lifted from its readme):

    var mock = require('mock-fs');
    
    mock({
      'path/to/fake/dir': {
        'some-file.txt': 'file content here',
        'empty-dir': {/** empty directory */}
      },
      'path/to/some.png': new Buffer([8, 6, 7, 5, 3, 0, 9]),
      'some/other/path': {/** another empty directory */}
    });
    
    0 讨论(0)
  • 2021-02-05 02:41

    Take a look at using-stubs, particularly at the require() part.

    Leave you module code as you would do normally, eg:

    //myApp.js
    var fs = require('fs');
    
    fs.readdir(path, function(err, files) {
      //Do something.
    });
    

    Then, on your tests module (or any unit testing framework), use using-stubs to modify (and even match or validate) the behaviour of fs:

    var using = require('using-stubs');
    
    //get a reference to require('fs')
    var fs = using.require('fs');
    
    //override behaviour of fs.readdir
    using(fs)('readdir').stub(function(path, callback){
    
        //override fs.readdir() logic
        var err = null;
        var files = [];
        // (...)
    
        //mock original behaviour
        callback(err, files)
    })
    
    //then run the app normally to test it (some frameworks do this for you)
    require('myApp')
    

    Now running your test will override the internal behaviour of fs in myApp.js, without needing to change code in either of the components.

    You can also do other cool stuff such as validating how many times methods are called, match exact method call parameters or scope, even override behaviour on new Class instances used internally in myApp.js.

    0 讨论(0)
  • 2021-02-05 02:41

    Use memfs in-memory filesystem.

    0 讨论(0)
  • 2021-02-05 02:52

    I like using rewire for stubbing out require(...) statements

    Module Under test

    module-a.js

    var fs = require('fs')
    function findFile(path, callback) {
      fs.readdir(path, function(err, files) {
         //Do something.
      })
    }
    

    Test Code

    module-a-test.js

    var rewire = require('rewire')
    var moduleA = rewire('./moduleA')
    // stub out fs
    var fsStub = {
      readdir: function(path, callback) {
         console.log('fs.readdir stub called')
         callback(null, [])
      }
    }
    moduleA.__set__('fs', fsStub)
    // call moduleA which now has a fs stubbed out
    moduleA()
    
    0 讨论(0)
提交回复
热议问题