【问题标题】:Why do I see "define not defined" when running a Mocha test with RequireJS?为什么在使用 RequireJS 运行 Mocha 测试时看到“未定义定义”?
【发布时间】:2012-03-07 10:02:07
【问题描述】:

我正在尝试了解如何开发独立的 Javascript 代码。我想编写带有测试和模块的 Javscript 代码,从命令行运行。所以我安装了node.jsnpm 以及库requirejsunderscoremocha

我的目录结构如下:

> tree .
.
├── node_modules
├── src
│   └── utils.js
└── test
    └── utils.js

src/utils.js 是我正在编写的一个小模块,代码如下:

> cat src/utils.js 
define(['underscore'], function () {

    "use strict";

    if ('function' !== typeof Object.beget) {
        Object.beget = function (o) {
            var f = function () {
            };
            f.prototype = o;
            return new f();
        };
    }

});

test/utils.js 是测试:

> cat test/utils.js 
var requirejs = require('requirejs');
requirejs.config({nodeRequire: require});

requirejs(['../src/utils'], function(utils) {

    suite('utils', function() {
        test('should always work', function() {
            assert.equal(1, 1);
        })
    })

});

然后我尝试从顶级目录运行(所以mocha 看到test 目录):

> mocha

node.js:201
        throw e; // process.nextTick error, or 'error' event on first tick
              ^
Error: Calling node's require("../src/utils") failed with error: ReferenceError: define is not defined
    at /.../node_modules/requirejs/bin/r.js:2276:27
    at Function.execCb (/.../node_modules/requirejs/bin/r.js:1872:25)
    at execManager (/.../node_modules/requirejs/bin/r.js:541:31)
    ...

所以我的问题是:

  • 这是构建代码的正确方法吗?
  • 为什么我的测试没有运行?
  • 学习这种东西的最好方法是什么?我很难在 Google 上找到好的示例。

谢谢...

[抱歉 - 暂时发布了错误代码的结果;已修复]

PS 我使用 requirejs 是因为我还想稍后从浏览器运行这段代码(或其中的一部分)。

更新/解决方案

下面的答案中没有的东西是我需要使用mocha -u tdd 来进行上面的测试样式。这是最终测试(也需要断言)及其用途:

> cat test/utils.js 

var requirejs = require('requirejs');
requirejs.config({nodeRequire: require});

requirejs(['../src/utils', 'assert'], function(utils, assert) {

    suite('utils', function() {
        test('should always work', function() {
            assert.equal(1, 1);
        })
    })

});
> mocha -u tdd

  .

  ✔ 1 tests complete (1ms)

【问题讨论】:

  • 为了让它工作,我需要安装 amdefine 模块并添加这些行 coffeescript =>> define = require('amdefine')(module) if (typeof define != 'function' )

标签: javascript node.js npm requirejs mocha.js


【解决方案1】:

我不使用requirejs,所以我不确定该语法是什么样的,但这是我在nodebrowser 中运行代码时所做的:

对于导入,确定我们是在节点还是浏览器中运行:

var root =  typeof exports !== "undefined" && exports !== null ? exports : window;

然后我们可以正确获取任何依赖项(如果在浏览器中它们已经可用,或者我们使用require):

var foo = root.foo;
if (!foo && (typeof require !== 'undefined')) {
    foo = require('./foo');
}

var Bar = function() {
    // do something with foo
}

然后任何需要被其他文件使用的功能,我们将其导出到根目录:

root.bar = Bar;

例如,GitHub 是一个很好的来源。去看看你最喜欢的库的代码,看看他们是怎么做的 :) 我用mocha 测试了一个可以在浏览器和节点中使用的 javascript 库。代码可在https://github.com/bunkat/later 获得。

【讨论】:

    【解决方案2】:

    您的测试未运行的原因是 src/utils.js 不是有效的 Node.js 库。

    根据 RequireJS 文档,为了与 Node.js 和 CommonJS 要求标准共存,您需要将 add a bit of boilerplate 放在 src/utils.js 文件的顶部,以便加载 RequireJS 的 define 函数。

    但是,由于 RequireJS 被设计为能够需要“经典”的面向 Web 浏览器的源代码,因此我倾向于将以下模式与我也希望在浏览器中运行的 Node.js 库一起使用:

    if(typeof require != 'undefined') {
        // Require server-side-specific modules
    }
    
    // Insert code here
    
    if(typeof module != 'undefined') {
        module.exports = whateverImExporting;
    }
    

    这样做的好处是不需要为其他 Node.js 用户提供额外的库,并且通常与客户端上的 RequireJS 配合使用。

    一旦您的代码在 Node.js 中运行,您就可以开始测试了。我个人仍然更喜欢 expresso 而不是 mocha,尽管它是后续测试框架。

    【讨论】:

    • 谢谢!我不认为你也知道我需要做什么来定义“套件”(来自 mocha),因为有了样板,我的测试现在失败了?另外,您是说“定义”不适用于浏览器代码吗?我不明白您采用上述替代方法的动机 - 它“只是”一个风格问题吗?
    • 啊,我明白你的意思了——你不想为网络浏览器定义节点的东西。
    • 这并不难,您缺少require('mocha') 样板。 Taken straight from the horse's mouth 就是答案。 (我是 StackOverflow 的新手,不知道 cmets 中不能有多行源代码。)
    • 再次感谢 - 可能要到周末才能回到这里,但你帮了大忙。
    • 以防万一有人在这里感到困惑 - 我的最后一个 mocha 问题的解决方案现在在上面描述,在问题的末尾(使用 -u tdd)。
    【解决方案3】:

    Mocha 文档缺乏关于如何设置这些东西的信息,而且由于它在后台执行的所有魔术技巧,所以很难弄清楚。

    我找到了使用 require.js 获取浏览器文件以在节点下的 Mocha 中工作的关键:Mocha 必须 使用 addFile 将文件添加到其套件中:

    mocha.addFile('lib/tests/Main_spec_node');
    

    其次,使用beforeEach 和可选的回调来异步加载你的模块:

    describe('Testing "Other"', function(done){
        var Other;
        beforeEach(function(done){
            requirejs(['lib/Other'], function(_File){
                Other = _File;
                done(); // #1 Other Suite will run after this is called
            });
        });
    
        describe('#1 Other Suite:', function(){
            it('Other.test', function(){
                chai.expect(Other.test).to.equal(true);
            });
        });
    });
    

    我为如何让这一切正常工作创建了一个引导程序:https://github.com/clubajax/mocha-bootstrap

    【讨论】:

    • +1 beforeEach中requirejs依赖加载的解决方案
    【解决方案4】:

    您正在尝试运行为浏览器 (AMD) 设计的 JS 模块,但在后端它可能无法正常工作(因为模块是以 commonjs 方式加载的)。因此,您将面临两个问题:

    1. 未定义定义
    2. 0 次测试运行

    在浏览器中define 将被定义。当你需要 requirejs 的东西时,它将被设置。但是 nodejs 以 commonjs 的方式加载模块。 define 在这种情况下没有定义。但它会在我们使用 requirejs 的时候定义!

    这意味着现在我们需要异步代码,它带来了第二个问题,异步执行的问题。 https://github.com/mochajs/mocha/issues/362

    这是一个完整的工作示例。 看我必须配置 requirejs (amd) 来加载模块,我们没有使用 require (node/commonjs) 来加载我们的模块。

    > cat $PROJECT_HOME/test/test.js
    
    var requirejs = require('requirejs');
    var path = require('path')
    var project_directory = path.resolve(__dirname, '..')
    
    requirejs.config({
      nodeRequire: require, 
      paths: {
        'widget': project_directory + '/src/js/some/widget'
      }
    });
    
    describe("Mocha needs one test in order to wait on requirejs tests", function() {
      it('should wait for other tests', function(){
        require('assert').ok(true);
      });
    });
    
    
    requirejs(['widget/viewModel', 'assert'], function(model, assert){
    
      describe('MyViewModel', function() {
        it("should be 4 when 2", function () {
            assert.equal(model.square(2),4)
        })
      });
    
    })
    

    对于您要测试的模块:

    > cat $PROJECT_HOME/src/js/some/widget/viewModel.js
    
    define(["knockout"], function (ko) {
    
        function VideModel() {
            var self = this;
    
            self.square = function(n){
                return n*n;
            }
    
        }
    
        return new VideModel();
    })
    

    【讨论】:

      【解决方案5】:

      以防David's answer不够清楚,我只需要添加这个:

      if (typeof define !== 'function') {
          var define = require('amdefine')(module);
      }
      

      在我使用 define 的 js 文件顶部,如 RequireJS 文档 ("Building node modules with AMD or RequireJS") 中所述,并在同一文件夹中添加 amdefine 包:

      npm install amdefine
      

      这将创建 node_modules 文件夹,其中包含 amdefine 模块。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-08-14
        • 1970-01-01
        • 2020-10-27
        • 2014-04-25
        • 2019-05-25
        • 2014-11-12
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多