Mocha + RequireJS = AMD testing

2019-03-18 04:11发布

问题:

I have a hard time connecting Mocha to RequireJS based application, may be You'll be able to come up with something :). After many hours when I've been trying to load AMD modules and simply console.log some 'fired' info that the module has been loaded... nothing happend had happened - the program just ended and printed out some mocha info.

var facade = requirejs(['../../public/js/scripts/widgets/widgets/article/main.js'],      
    function(mod) {
        console.log('fired')
});
// run with: $ mocha -u tdd test.js --reporter spec

and than I've come up with the idea to fire just this to test callbacks:

setTimeout((function() {
    console.log('fired');
}), 5000);
// run with: $ mocha -u tdd test.js --reporter spec

also didn't work. So finally I've run both with

$ node test.js 

and finally it worked. So question is than: How to run Mocha test with callbacks handling, as those are essential for AMD testing?

回答1:

The way you are doing it, mocha is not going to do anything with your file because it does not see a test suite in it. RequireJS is scheduled to call the callback but mocha exits before this has a chance to happen. Same with your timeout example.

The following gives you an example.

File test.js:

'use strict';
var requirejs = require("requirejs");
requirejs.config({
    baseUrl: '.',
    nodeRequire: require
});

suite('Something', function(){
    var foo;

    suiteSetup(function (done){
        // This saves the module foo for use in tests. You have to use
        // the done callback because this is asynchronous.
        requirejs(['foo'],
                  function(mod) {
            console.log("fired!");
            foo = mod;
            done();
        });
    });

  suite('blah', function(){
    test('blah', function(){
      if (foo.test !==  "test")
          throw new Error("failed!");
    });
  });
});

File foo.js:

define(function () {
   return {test: "test"};
});

When you run:

mocha -u tdd test.js

You'll see that the callback is fired and the test passes.

For the benefit of people reading this question and confused by the use of suite, suiteSetup, test... Mocha supports multiple interfaces. The code here is using the TDD interface (the OP invokes Mocha with -u tdd), which exports suite, suiteSetup, test, etc. In the default BDD interface, the equivalents are describe, before and it, respectively.



回答2:

I have configured related boilerplate for using mocha in environment of RequireJS. It may be not exact what you want, but it may be helpful. https://github.com/x2es/boilerplate-karma-mocha-chai-requirejs

One more note - assuming that your script placed in "/public" it makes sense to test it in browser environment instead nodejs. For this purposes you should to look at some test-runner like JsTestDriver (https://code.google.com/p/js-test-driver/) or karma-runner (http://karma-runner.github.io/). Or another...

In snipped provided in karma documentation (http://karma-runner.github.io/0.8/plus/RequireJS.html)

var tests = [];
for (var file in window.__karma__.files) {
  if (window.__karma__.files.hasOwnProperty(file)) {
    if (/Spec\.js$/.test(file)) {
      tests.push(file);
    }
  }
}

requirejs.config({
    // Karma serves files from '/base'
    baseUrl: '/base/src',

    paths: {
        'jquery': '../lib/jquery',
        'underscore': '../lib/underscore',
    },

    shim: {
        'underscore': {
            exports: '_'
        }
    },

    // ask Require.js to load these files (all our tests)
    deps: tests,

    // start test run, once Require.js is done
    callback: window.__karma__.start
});

introduced way when we force requirejs to preload all necessary spec-files using

require.config({
  deps: ['array', 'of', 'our', 'spec', 'files']
})

In this environment each spec-file should be a regular RequireJS module.

Example of test spec for such environment:

define(['chai'], function(chai) {
  var expect = chai.expect;

  describe('bootstrap', function() {
    it('should...', function() {
      expect('a').to.equal('a');
    });
  });
});