使用Karma运行Mocha单元测试 - done()未定义

11

我正在尝试使用Mocha编写的测试在Karma中运行,虽然它们有点能够工作,但我无法使用done()方法来实现异步测试,这使得这些工具对我来说几乎无用。我错过了什么?

karma.conf.js

module.exports = function(config) {
  config.set({
    basePath: '../..',
    frameworks: ['mocha', 'requirejs', 'qunit'],
    client: {
        mocha: {
            ui: 'bdd'
        }
    },
    files: [
      {pattern: 'libs/**/*.js', included: false},
      {pattern: 'src/**/*.js', included: false},
      {pattern: 'tests/mocha/mocha.js', included: false},
      {pattern: 'tests/should/should.js', included: false},
      {pattern: 'tests/**/*Spec.js', included: false},
      'tests/karma/test-main.js'
    ],
    exclude: [
      'src/main.js'
    ],
    // test results reporter to use
    // possible values: 'dots', 'progress', 'junit', 'growl', 'coverage'
    reporters: ['progress', 'dots'],
    port: 9876,
    colors: true,
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_WARN,
    autoWatch: true,
    // Start these browsers, currently available:
    // - Chrome
    // - ChromeCanary
    // - Firefox
    // - Opera (has to be installed with `npm install karma-opera-launcher`)
    // - Safari (only Mac; has to be installed with `npm install karma-safari-launcher`)
    // - PhantomJS
    // - IE (only Windows; has to be installed with `npm install karma-ie-launcher`)
    browsers: ['Chrome'],
    // If browser does not capture in given timeout [ms], kill it
    captureTimeout: 60000,
    // Continuous Integration mode
    // if true, it capture browsers, run tests and exit
    singleRun: false
  });
};

测试主文件test-main.js(配置RequireJS)

var allTestFiles = [];
var pathToModule = function(path) {
  return path.replace(/^\/base\//, '../').replace(/\.js$/, '');
};

Object.keys(window.__karma__.files).forEach(function(file) {
  if (/Spec\.js$/.test(file)) {
    // Normalize paths to RequireJS module names.
    allTestFiles.push(pathToModule(file));
  }
});

require.config({
  // Karma serves files under /base, which is the basePath from your config file
  baseUrl: '/base/src',
  paths: {
    'should': '../tests/should/should',
    'mocha': '../tests/mocha/mocha',
    'pubsub': '../libs/pubsub/pubsub',
    'jquery': '../libs/jquery/jquery-1.10.2',
    'jquery-mobile': '//code.jquery.com/mobile/1.4.2/jquery.mobile-1.4.2.min'
  },
  // dynamically load all test files
  deps: allTestFiles, 
  // we have to kickoff jasmine, as it is asynchronous
  callback: window.__karma__.start
});

测试/fooSpec.js

define(['music/note'], function(Note) {

describe('nothing', function(done) {
    it('a silly test', function() {
        var note = new Note;
        note.should.not.eql(32);
    });
    done();
});
...

虽然这只是一个人为制造的例子,但如果我删除done()函数调用,它就会成功。现在的状态下,我得到了以下错误:

Uncaught TypeError: undefined is not a function
at /Library/WebServer/Documents/vg/tests/mocha/fooSpec.js:8

这是done()行,为什么会未定义?我不明白在哪里配置Mocha(或使用哪些选项)。是否有某种全局命名空间或元编程魔法导致RequireJS干扰了Mocha?

我在OS X 10.9.2上的Chrome 33中运行测试,如果相关,那么这可能有关。我花费了大量时间,已经准备放弃自动化测试:( - 在QUnit/Karma/RequireJS中也遇到了类似的问题,并且一直没有找到任何成功自动化测试的替代方法。我感觉像个傻瓜。

3个回答

17
在 Mocha 中,done 回调函数用于 itbeforeafterbeforeEachafterEach。因此:

describe('nothing', function() {
    it('a silly test', function(done) {
        var note = new Note;
        note.should.not.eql(32);
        done();
    });
});

这里是文档


2
你在这个例子中运行的测试不需要done()回调函数。这不是异步的。需要使用done回调函数的示例是...
describe('Note', function() {
    it('can be retrieved from database', function(done) {
        var note = new Note();
        cb = function(){
           note.contents.should.eql("stuff retrieved from database");
           done()
        }
        //cb is passed into the async function to be called when it's finished
        note.retrieveFromDatabaseAsync(cb)
    });
});

你的测试不应该有 done 回调函数。
describe('nothing', function() {
    it('umm...', function() {
        var note = new Note;
        note.should.not.eql(32);
    });

});

只有 'it' 函数提供了一个完成回调函数。而 describe 函数没有。你的问题与 karma 无关。你的 mocha 测试定义不正确。


我明白了。在编写任何真正的测试之前,我只是在尝试API,所以一开始很惊讶done没有被定义。当我将我的测试设置为异步时,更令我惊讶的是它似乎被定义了。没关系,我已经解决了这个问题,并编写了很多测试。 - Jason Boyd

1

天啊,这个 %$#@!

我永远不会想到这会出错:

describe('nothing', function(done) {
    it('umm...', function() {
        var note = new Note;
        note.should.not.eql(32);
    });
    done(); // throws error that undefined is not a function
});

但这样做完全没有问题:

describe('nothing', function(done) {
    it('umm...', function() {
        var note = new Note;
        note.should.not.eql(32);
    });
    setTimeout(function() {
        done();  // MAGIC == EVIL.
    }, 1000);
});

1
所以,我无法在几天内关闭自己的问题。但也许我永远不会这样做。也许这个问题应该永远不会被标记为已回答。感谢那些我永远不会拥有的日子;请添加一些文档来解释下一个可怜的家伙遇到这些恶作剧。 - Jason Boyd
我怀疑你说的版本实际上会生成一个异常,最终被某些东西吞掉。当它执行摩卡时,肯定是这样的。 - Louis
好的,但它遵循文档中的工作方式。测试按预期通过/失败。您是否建议实现为不向它()、before()等传递任何内容,并异步(仅)捕获异常,如果客户端代码调用了undefined()? - Jason Boyd
我已经验证它确实做到了这一点。也就是说,它会静默地捕获异步抛出的错误(在测试用例返回后),这样你就可以在describe()用例中使用done(),或者至少看起来是这样。我不确定这是否是期望的行为... - Jason Boyd
你看了我写的答案吗? - Louis
我会将你的回答标记为正确,但是说实话,我认为为什么Mocha被设计成这样行为的问题才是关键。并不是我在抱怨——因为我已经让它全部工作了,我感觉自己处于测试天堂。谢谢@Louis。 - Jason Boyd

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