javascript 如何在 mocha 测试中模拟全局变量(定义、模块、窗口)?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28334692/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-28 08:48:50  来源:igfitidea点击:

How to mock global variables (define, module, window) in mocha tests?

javascriptnode.jsunit-testingmocha

提问by SteamDev

In the pursuit of 100% code coverage, I am attempting to use mocha to test that my javascript module is loading correctly under AMD, CommonJS/Node, and browserconditions. The pattern I'm using is below:

在追求100%的代码覆盖率的,我试图用摩卡来测试我的javascript模块下正确加载AMDCommonJS/Nodebrowser条件。我使用的模式如下:

my-module.js

我的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);

Since I am running my tests with node, defineis never defined, and moduleis always defined; so the "CommonJS/Node" condition is the only one that ever gets tested.

由于我使用 node 运行我的测试,define因此从未定义,并且module始终已定义;所以“CommonJS/Node”条件是唯一经过测试的条件。

What I have tried so far is something like this:

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

my-module.test.js

我的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
    });
  });
});

I'm using spies to check if definehas been called, but the module doesn't show any signs of ever being reloaded with defineavailable to it. How can I achieve that?

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

And is there a safe way of nullifying moduleso that I can test the browser condition as well?

是否有一种安全的无效方法,module以便我也可以测试浏览器条件?

采纳答案by SteamDev

I was able to create a custom solution (borrowing the majority of this code from http://howtonode.org/testing-private-state-and-mocking-deps)

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

module-loader.js

模块加载器.js

This module basically creates a new context in which your custom properties are available in the same global space as requireand console, etc.

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

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

我的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

我的module.js

(function(global){

  function MyClass(){}

  if(typeof mock !== 'undefined'){ 
    mock("testing"); // will log "mock: testing"
  }

  module.exports = MyClass;

})(this);

回答by Kevin Ennis

You might want to check out the rewire module. I'm not 100% sure, but I think it'll let you do what you need.

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

https://github.com/jhnns/rewire

https://github.com/jhnns/rewire

回答by ShadSterling

Here's a lightweight solution that worked for me:

这是一个对我有用的轻量级解决方案:

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

Unfortunately, this has to go in the file being tested, would be cumbersome for cases where more of document's functionality is used, and doesn't help with needing test cases where something is undefined. But for simple cases, here's a simple solution.

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

(This is the answer I was looking for when I found this question)

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