为什么我看到"定义未定义"使用RequireJS运行Mocha测试时?

时间:2012-02-16 00:45:39

标签: javascript node.js npm requirejs mocha

我试图了解如何开发独立的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)
    ...

所以我的问题是:

  • 这是构造代码的正确方法吗?
  • 为什么我的测试没有运行?
  • 学习这类东西的最佳方法是什么?我很难在谷歌找到好的例子。

...谢谢

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

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)

5 个答案:

答案 0 :(得分:18)

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

根据RequireJS文档,为了与Node.js和CommonJS require标准共存,您需要add a bit of boilerplatesrc/utils.js文件的顶部,以便RequireJS的{{1}函数已加载。

然而,由于RequireJS旨在能够要求“经典”面向Web浏览器的源代码,我倾向于使用我想在浏览器中运行的Node.js库使用以下模式:

define

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

在Node.js中运行代码后,即可开始测试。我个人仍然更喜欢表达摩卡,尽管它是后继测试框架。

答案 1 :(得分:4)

Mocha文档缺乏如何设置这些内容,并且由于它在幕后所做的所有魔术技巧而难以弄清楚。

我找到了使用 require.js 在节点下的Mocha中使用浏览器文件的关键:Mocha 将文件添加到{{1 }}:

addFile

第二,使用mocha.addFile('lib/tests/Main_spec_node'); 和可选的回调来异步加载模块:

beforeEach

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

答案 2 :(得分:1)

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

  1. define未定义
  2. 0测试运行
  3. 将在浏览器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();
    })
    

答案 3 :(得分:0)

如果David's answer不够明确,我只需要添加:

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

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

npm install amdefine

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

答案 4 :(得分:-1)

我不使用requirejs因此我不确定该语法是什么样的,但这是我在nodebrowser内运行代码所做的工作:< / p>

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

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:

root.bar = Bar;

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