【问题标题】:Testing Node.js, mock out and test a module that has been required?测试 Node.js,模拟并测试所需的模块?
【发布时间】:2012-03-22 18:25:21
【问题描述】:

我正在努力围绕我的节点模块编写高质量的测试。问题是需要模块系统。我希望能够检查某个必需的模块是否具有方法或其状态已更改。似乎有 2 个相对较小的库可以在这里使用:node-gentlymockery。但是,由于他们的“低调”,这让我觉得要么人们不对此进行测试,要么还有另一种我不知道的方法。

模拟和测试所需模块的最佳方法是什么?

【问题讨论】:

    标签: unit-testing testing node.js mocking require


    【解决方案1】:

    ----------- 更新 ---------------

    node-sandbox 的工作原理与下文所述相同,但包含在一个不错的模块中。我发现它非常适合使用。


    --------------- 详细的遮阳篷 ---------------

    经过多次试验,我发现在模拟事物的同时单独测试节点模块的最佳方法是使用 Vojta Jina 的方法在具有新上下文的虚拟机内运行每个模块,如 here 所述。

    使用这个测试 vm 模块:

    var vm = require('vm');
    var fs = require('fs');
    var path = require('path');
    
    /**
     * Helper for unit testing:
     * - load module with mocked dependencies
     * - allow accessing private state of the module
     *
     * @param {string} filePath Absolute path to module (file to load)
     * @param {Object=} mocks Hash of mocked dependencies
     */
    exports.loadModule = function(filePath, mocks) {
      mocks = mocks || {};
    
      // this is necessary to allow relative path modules within loaded file
      // i.e. requiring ./some inside file /a/b.js needs to be resolved to /a/some
      var resolveModule = function(module) {
        if (module.charAt(0) !== '.') return module;
        return path.resolve(path.dirname(filePath), module);
      };
    
      var exports = {};
      var context = {
        require: function(name) {
          return mocks[name] || require(resolveModule(name));
        },
        console: console,
        exports: exports,
        module: {
          exports: exports
        }
      };
    
      vm.runInNewContext(fs.readFileSync(filePath), context);
      return context;
    };
    

    可以使用自己的上下文测试每个模块,并轻松排除所有外部依赖项。

    fsMock = mocks.createFs();
    mockRequest = mocks.createRequest();
    mockResponse = mocks.createResponse();
    
    // load the module with mock fs instead of real fs
    // publish all the private state as an object
    module = loadModule('./web-server.js', {fs: fsMock});
    

    我强烈推荐这种方式来单独编写有效的测试。只有验收测试才能触及整个堆栈。单元和集成测试应该测试系统的独立部分。

    【讨论】:

    • 感谢您的出色回答!但是我还有另一个问题:我无法利用这种技术在正在测试的模块中用我自己的自定义函数覆盖私有函数。它产生两个函数:一个在本地范围内,另一个在全局范围内,被测模块始终调用全局/原始版本。
    【解决方案2】:

    我认为嘲讽模式很好。也就是说,我通常选择将依赖项作为参数发送给函数(类似于在构造函数中传递依赖项)。

    // foo.js
    module.exports = function(dep1, dep2) {
        return {
            bar: function() {
                // A function doing stuff with dep1 and dep2
            }
        }
    }
    

    在测试时,我可以发送模拟、空对象,任何看起来合适的东西。请注意,我不会对所有依赖项都执行此操作,基本上只针对 IO ——我觉得不需要测试我的代码调用 path.join 或其他什么。

    我认为让你紧张的“低调”是由于以下几点:

    • 有些人的代码结构与我的相似
    • 有些人有自己的助手来实现与 mockery 等人相同的目标(这是一个非常简单的模块)
    • 有些人不对这些东西进行单元测试,而是启动他们的应用程序(和数据库等)的一个实例并针对它进行测试。更干净的测试,服务器速度非常快,不会影响测试性能。

    简而言之,如果你认为嘲讽适合你,那就去吧!

    【讨论】:

    • 这个问题是单元测试击中了不理想的数据库。
    • @beck:只有在测试时发送一个真实的数据库。
    【解决方案3】:

    您可以使用“a”轻松模拟 require:https://npmjs.org/package/a

    //Example faking require('./foo') in unit test:
    var fakeFoo = {};
    var expectRequire = require('a').expectRequire;
    expectRequire('./foo).return(fakeFoo);
    
    
    //in sut:
    var foo = require('./foo); //returns fakeFoo
    

    【讨论】:

      猜你喜欢
      • 2015-02-05
      • 2018-11-26
      • 2013-03-25
      • 2016-12-23
      • 1970-01-01
      • 2012-05-09
      • 2016-02-19
      • 1970-01-01
      • 2021-08-27
      相关资源
      最近更新 更多