Node.js,Mocha,使闭包中的全局变量可用

时间:2013-12-19 13:22:46

标签: javascript node.js mocha

我目前正在使用Node设置一些mocha测试,一般来说它们都可以工作。我现在遇到了一个我无法解决的问题。

我有一个包含以下内容的JS文件:MyClass.js (class MyClass + constructor: ->)的常规CoffeeScript输出

编辑:这是浏览器代码,我只想用Node来测试它。 (这甚至是可取的吗?)

(function() {

  window.MyClass = (function() {

    function MyClass() {
      // Do something cool here
    }

    return MyClass;

  })();

}).call(this);

我现在在我的测试文件中需要MyClass.js。一旦我运行它,它会直接抛出错误

TESTFILE:

var myclass = require('MyClass.js');
...
describe('MyClass', function() { ... });

错误:

ReferenceError: window is not defined.

到目前为止,我理解为什么会发生这种情况,Node中不存在窗口。但我无法想出一个解决方案。我实际上并不特别需要真正的window对象,所以我想嘲笑它就足够了。但它不是......

var window = {},
    myclass = require('myclass.js');
...
describe('MyClass', function() { ... });

此命令也没有帮助:$ mocha --globals window

我仍然以同样的错误结束。 任何想法都非常感谢!

5 个答案:

答案 0 :(得分:3)

您实际上并不想要窗口对象,您想要的是全局对象。以下是一些可以在浏览器中获取的代码(在这种情况下,它将与' window'相同)或在节点中(在这种情况下它将与' global&#39相同) ;)

var global = Function('return this')();

然后设置内容,而不是在'窗口上。

注意:还有其他获取全局对象的方法,但这样做的好处是它也可以在严格模式代码中运行。

答案 1 :(得分:2)

使用以下代码,您可以在Web浏览器环境和Node.js中使用类似类的对象而无需修改。 (抱歉,我不知道如何将其翻译成CoffeeScript)

(function (exports) {

    var MyClass = (function() {

        function MyClass() {
            // Do something cool here
        }

        return MyClass;

    })();

    exports(MyClass);

})(function (exported) {
    if (typeof module !== 'undefined' && module.exports) {

        module.exports = exported;

    } else if (typeof window !== 'undefined') {

        window.MyClass = exported;

    } else {

        throw new Error('unknown environment');

    }
});

由于您已经有一个不会污染全局名称空间的范围,您可以将其缩小为:

(function (exports) {

    function MyClass() {
        // Do something cool here
    }

    exports(MyClass);

})(function (exported) {

    // see above

});

我不是AMD,require.js和其他模块加载器的专家,但我认为应该很容易扩展这种模式以支持其他环境。

修改

在评论中,您说上述解决方案在转换回CoffeeScript时无效。因此,我建议另一种解决方案。我没有尝试过,但也许这可能是解决问题的方法:

global.window = {}; // <-- should be visible in your myclass.js

require('myclass.js');

var MyClass = global.window.MyClass;

describe('MyClass', function() {
    var my = new MyClass();
    ...
});

这是一段可怕的代码,但如果它有效,可能出于测试目的就足够了。

由于node.js的模块加载行为,仅当您的require('myclass.js')是节点进程中此文件的第一个要求时,此方法才有效。但是在使用Mocha进行测试时,这应该是真的。

答案 2 :(得分:1)

1)您正在寻找的是module.exports来揭示Node中的内容:

http://openmymind.net/2012/2/3/Node-Require-and-Exports/

2)你也不需要Node中的IIFE,你可以删除(function() {...

3)你总是可以在Github上查看一些流行的Node repo来查看示例,看看Mocha代码,因为你正在使用它,你将学到一两件事。

答案 3 :(得分:1)

jsdom这样的东西比PhantomJS轻,但是提供了一些测试代码的东西,这些代码需要运行正确的window。我used it非常成功地测试了在DOM树中上下导航的代码。

你问:

  

这是浏览器代码,我只想用Node来测试它。 (这甚至是可取的吗?)

这是非常可取的。有一点像jsdom这样的解决方案不会削减它,但只要你的代码在jsdom处理的范围内,就可以使用它并将启动测试环境的成本保持在最低限度。

答案 4 :(得分:1)

@hgoebl:由于我不是OP,我无法添加他原来的CoffeeScript代码,但这是我的例子:

pubsub.coffee:

window.PubSub = window.PubSub || {}

PubSub.subscribe = ( subject, callback )->

现在测试:

assert = require "assert"
pubsub = require './pubsub.coffee'

describe "pubsub.http interface", ->

    it "should perform a http request", ->
        PubSub.subscribe 1, 2

到目前为止对我有用的是:

window.PubSub = window.PubSub || {}

window.PubSub.subscribe = ( subject, callback )->

和测试:

`window = {}`

assert = require "assert"
pubsub = require './pubsub.coffee'

describe "pubsub.http interface", ->

    it "should perform a http request", ->
        window.PubSub.subscribe 1, 2

解决方案的主要缺点是,我必须在实现和测试中明确提到window对象。在浏览器中执行的用户代码应该能够省略它。

我现在想出了另一个解决方案:

window        = window || exports
window.PubSub = window.PubSub || {}
PubSub        = PubSub || window.PubSub

PubSub.subscribe = ( subject, callback )->

然后在测试中,只需要PubSub命名空间:

PubSub = require( './pubsub.coffee' ).PubSub

最后,kybernetikos应用的解决方案看起来像这样:

global = `Function('return this')()`
global.PubSub = global.PubSub || {}

PubSub.subscribe = ( subject, callback )->

现在,PubSub名称空间位于全局名称空间中,只需要包含mocha测试的文件中的简单需求:

require( './pubsub.coffee' )