当我使用RequireJS运行Mocha测试时,为什么会出现"define not defined"的提示?

37

我正在尝试理解如何开发独立的JavaScript代码。 我希望编写具有测试和模块的JavaScript代码,并从命令行运行。 因此,我已经安装了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)
    ...

那么我的问题是:

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

谢谢...

[抱歉-短暂地发布了错误代码的结果;现已修正]

PS:我正在使用requirejs,因为我还想以后从浏览器中运行此代码(或其中的一部分)。

更新/解决方案

一个未在下面的答案中提到的问题是,我需要使用mocha -u tdd来测试上述样式。以下是最终测试(还需要使用assert)及其用法:

> 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') <<=添加到我的“类”文件中以使其工作(而不是测试文件)。这有点烦人,因为需要在测试后删除此任务。 - devric
5个回答

18
你的测试无法运行是因为 src/utils.js 不是有效的 Node.js 库。
根据 RequireJS 文档,为了与 Node.js 和 CommonJS require 标准共存,你需要在 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。

1
谢谢!我不确定你是否知道我需要做什么才能定义“suite”(来自Mocha),因为使用样板文件后,我的测试现在失败了。此外,您是说“define”对于浏览器代码无效吗?我不理解您采用上述替代方法的动机-这只是一种风格问题吗? - andrew cooke
啊,我明白你的意思了 - 你不想为 Web 浏览器定义节点内容。 - andrew cooke
这并不太难,你错过了 require('mocha') 样板文件。从权威渠道获取的答案。(我是StackOverflow的新手,不知道在评论中不能有多行源代码。) - user1207456
再次感谢 - 可能要等到周末才能回复,但你已经帮了我很大的忙。 - andrew cooke
如果有人在这里并感到困惑 - 我的最后一个mocha问题的解决方案现在已经在问题末尾描述(使用-u tdd)。 - andrew cooke
请不要通过破坏您的帖子来给他人带来更多工作。通过在 Stack Exchange(SE)网络上发布内容,您已授予非撤销权利,在CC BY-SA 4.0许可证下,使SE可以分发该内容(即使您将来做出其他选择)。按照SE政策,非破坏性版本将被分发。因此,任何破坏行为都将被还原。请参见:删除功能如何工作?…。如果允许删除,则在帖子下方左侧有一个“删除”按钮,但它仅适用于浏览器,而不适用于移动应用程序。 - Makyen

5

Mocha文档缺乏关于如何设置这些内容的说明,由于其在幕后执行所有魔术技巧,因此很难弄清楚。

我发现使用require.js在Node下使浏览器文件工作的关键是:必须使用addFile将文件添加到Mocha套件中:

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

我为如何让这一切运作起来创建了一个基于Bootstrap的模板: https://github.com/clubajax/mocha-bootstrap

在 beforeEach 中解决 requirejs 依赖加载问题的解决方案 +1 - bennidi

1
您正在尝试运行为浏览器设计的JS模块(AMD),但在后端可能无法正常工作(因为模块以commonjs方式加载)。因此,您将面临两个问题:
  1. 未定义define
  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();
})

0

如果David的回答不够清晰,我需要补充一下:

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

在使用define的js文件顶部,如RequireJS文档("使用AMD或RequireJS构建节点模块")中所述,在同一文件夹中添加amdefine包:
npm install amdefine

这将创建一个名为node_modules的文件夹,并在其中包含amdefine模块。


-1

我不使用requirejs,所以我不确定那个语法是什么样子的,但这是我在node浏览器中运行代码的方法:

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

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上获取。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接