2015-02-05 51 views
3

在追求100%的代码覆盖率的,我试图用摩卡来测试我的javascript模块AMD下正确加载,CommonJS/Node,并browser条件。我使用的模式如下:如何在mocha测试中模拟全局变量(定义,模块,窗口)?

我-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”条件是唯一经过测试的条件。

我至今尝试过是这样的:

我-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,以便我可以测试浏览器的情况?

回答

0

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

模块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; 
}; 

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

我-module.js

(function(global){ 

    function MyClass(){} 

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

    module.exports = MyClass; 

})(this); 
3

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

https://github.com/jhnns/rewire

+0

感谢您的答案!这看起来很有希望,明天就会测试一下! – SteamDev

+1

有用的模块,但在这种特殊情况下没有雪茄。我认为这可能是因为我需要全局可用于包装IIFE,而不是导出的类本身。 – SteamDev

-2

这里是为我工作一个轻量级的解决方案:

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

不幸的是,这必须在正在测试的文件中进行,在使用更多document的功能的情况下会很麻烦,并且不需要测试用例,其中某些东西是未定义的。但对于简单的情况,这是一个简单的解决方案。

(这是我找到的答案,当我发现这个问题)