【问题标题】:How to mock global variables (define, module, window) in mocha tests?如何在 mocha 测试中模拟全局变量(定义、模块、窗口)?
【发布时间】:2015-04-04 18:15:37
【问题描述】:

为了追求 100% 的代码覆盖率,我正在尝试使用 mocha 来测试我的 javascript 模块在 AMDCommonJS/Node 下是否正确加载,和 browser 条件。我使用的模式如下:

my-module.js

(function(global){

  function MyClass(){}

  // AMD
  if(typeof define === 'function' && define.amd){
    define(function(){
      return MyClass;
    });

  // CommonJS/Node
  } else if (typeof module !== 'undefined' && module.exports){
    module.exports = MyClass;

  // Browser
  } else {
    global.MyClass = MyClass;
  }

})(this);

由于我使用节点运行我的测试,define 从未定义,module 始终被定义;所以“CommonJS/Node”条件是唯一经过测试的条件。

到目前为止我尝试过的是这样的:

my-module.test.js

var MyClass = require('./my-module');

describe('MyClass', function(){
  // suite of tests for the class itself
  // uses 'var instance = new MyClass();' in each test
  // all of these tests pass
});

describe('Exports', function(){
  // suite of tests for the export portion
  beforeEach(function(){
    MyClass = null; // will reload module for each test
    define = null; // set 'define' to null
    module = null; // set 'module' to null
  });

  // tests for AMD
  describe('AMD', function(){
    it('should have loaded as AMD module', function(){
      var define = function(){};
      define.amd = true;

      MyClass = require('./my-module'); // might be cached?
      // hoping this reloads with 'define' in its parent scope
      // but it does not. AMD condition is never reached.

      expect(spy).to.have.been.called(); // chai spy, code omitted
    });
  });
});

我正在使用间谍检查define 是否已被调用,但该模块没有显示出任何迹象表明曾经使用可用的define 重新加载。我怎样才能做到这一点?

有没有一种安全的方法可以取消module,以便我也可以测试浏览器的状况?

【问题讨论】:

    标签: javascript node.js unit-testing mocha.js


    【解决方案1】:

    您可能想查看重新布线模块。我不是 100% 确定,但我认为它会让你做你需要的。

    https://github.com/jhnns/rewire

    【讨论】:

    • 感谢您的回答!这看起来很有希望,明天将进行测试!
    • 有用的模块,但在这种特殊情况下没有雪茄。我认为这可能是因为我需要包装 IIFE 可用的全局变量,而不是导出的类本身。
    【解决方案2】:

    我能够创建自定义解决方案(从 http://howtonode.org/testing-private-state-and-mocking-deps 借用大部分代码)

    module-loader.js

    这个模块基本上创建了一个新的上下文,您的自定义属性在与requireconsole 等相同的全局空间中可用。

    var vm = require('vm');
    var fs = require('fs');
    var path = require('path');
    var extend = require('extend'); // install from npm
    
    /**
     * 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
        }
      };
    
      var extendMe = {};
      extend(true, extendMe, context, mocks);
    
      // runs your module in a VM with a new context containing your mocks
      // http://nodejs.org/api/vm.html#vm_vm_runinnewcontext_code_sandbox_filename
      vm.runInNewContext(fs.readFileSync(filePath), extendMe);
    
      return extendMe;
    };
    

    my-module.test.js

    var loadModule = require('./module-loader').loadModule;
    
    // ...
    
    it('should load module with mocked global vars', function(){
      function mockMethod(str){
        console.log("mock: "+str);
      }
    
      var MyMockModule = loadModule('./my-module.js', {mock:mockMethod});
      // 'MyClass' is available as MyMockModule.module.exports
    });
    

    my-module.js

    (function(global){
    
      function MyClass(){}
    
      if(typeof mock !== 'undefined'){ 
        mock("testing"); // will log "mock: testing"
      }
    
      module.exports = MyClass;
    
    })(this);
    

    【讨论】:

      【解决方案3】:

      这是一个适合我的轻量级解决方案:

      let document = (typeof document === "undefined") ? {} : document;
      

      不幸的是,这必须放在被测试的文件中,对于使用更多document 的功能的情况会很麻烦,并且对于需要未定义的测试用例没有帮助。但是对于简单的情况,这里有一个简单的解决方案。

      (这是我发现这个问题时正在寻找的答案)

      【讨论】:

        猜你喜欢
        • 2018-11-12
        • 2020-10-09
        • 1970-01-01
        • 2018-12-15
        • 1970-01-01
        • 1970-01-01
        • 2018-11-21
        • 2017-12-16
        • 2020-06-13
        相关资源
        最近更新 更多