2015-02-05 79 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的功能的情況下會很麻煩,並且不需要測試用例,其中某些東西是未定義的。但對於簡單的情況,這是一個簡單的解決方案。

(這是我找到的答案,當我發現這個問題)