旧それなりブログの跡地、画像やスタイルやJSなどが壊れてることがあります。

AMD準拠のmochaテストをWebとCUIで共通化

2013年7月15日

AMD に準拠して書いた、このような mocha テストケースを ..

define([
  "module-name"
], function(
  moduleName
){
  descrive("A title", function(){
    it("A test", function(){
    });
  });
};

以下の環境で使い回すためにやったことのまとめです。

  • RequireJS を介して読み込み、Web ブラウザから実行する
  • node.js の mocha コマンドから実行する

問題点: mocha コマンドから RequireJS を使うと不具合

とりあえず、Web で RequireJS を介して使う場合は問題有りません、普通に使えます。
問題になったのは、CUI から、つまり mocha コマンドから読み込んだ場合です。

まず、RequireJS は Node.js からの使用もサポートしており、
基本的には、AMD 準拠で定義しても困ることはありません。

例えば、以下のコードを node から動かした場合は、正常に動きます。

use_requirejs.js:

var define = requirejs = require("requirejs");

requirejs.config({
  baseUrl: __dirname,
  nodeRequire: require
});

define([
  "some-module"
], function(
  someModule
){
  console.log("OK");
});

実行結果:

$ node use_requirejs.js
OK

しかしながら、以下の mocha テストケースを mocha コマンドから実行すると、
テストが全く実行されません。

mocha_test.js:

var define = requirejs = require("requirejs");

requirejs.config({
  baseUrl: __dirname,
  nodeRequire: require
});

define([
  "expect.js"
], function(
  expect
){
  describe("Did it run?", function(){
    it("expect is function", function(){
      expect(expect).to.be.a("function");
    });
  });
});

実行結果:

$ mocha mocha_test.js

  

  0 passing (1 ms)  // 期待していたのは 1 passing

原因と解決策その1

原因は、非同期処理の関連ではあると思いますが、詳細は不明です。
解決策のその1としては、テストをこのように書き直すと正常に動くようになります。

mocha_test_2.js:

var define = requirejs = require("requirejs");

requirejs.config({
  baseUrl: __dirname,
  nodeRequire: require
});

describe("Did it run?", function(){
  it("expect is a function", function(){
    // テスト内でモジュールを読み込む
    define([
      "expect.js"
    ], function(
      expect
    ) {
      expect(expect).to.be.a("function");
    });
  }); 
}); 

実行結果:

$ mocha mocha_test_2.js

  ․

  1 passing (6 ms)

参考:
using mocha with requirejs v2.1.0 in node

Answer 2: Change RequireJS to amdefine!

上記の方法では、各テストケースに影響が出てしまうので、良い方法とはいえません。

そこでもう一つの方法としては、
node 用の AMD ローダーとしては、RequireJS は止めて、
amdefine という別のローダーを使うことです。

mocha_test_3.js:

// Don't forget "(module)"!
var define = require("amdefine")(module);

define([
  "expect.js"
], function(
  expect
) {
  describe("Did it run?", function(){
    it("expect is a function", function(){
      expect(expect).to.be.a("function");
    });
  });
});

実行結果:

mocha mocha_test_3.js

  ․

  1 passing (6 ms)

しかも何と、この amdefine は、RequireJS と同じ作者さんのものです!
安心感あるあるー!

テストランナーを作る必要がある

これに関しては完全にオレオレですが、
自分のプロジェクトでは、このようなテストランナーを作って
mocha コマンドから実行させています。

mochaから呼ぶのはこれだけにしている:

$ mocha runner.js

RequireJS 側の paths 設定と同期するためにひと処理入れたり、
expect.js や sinon などを、グローバル領域に展開したりしています。

この辺のノウハウをお持ちの方が居ましたら、
是非参考リポジトリを教えていただけると有り難いです。

蛇足: 何故 node から実行したいの?

(ヘッドレス)ブラウザを介すよりも断然実行が早くて気持ち良いということと、
自分がCUIの方が慣れているから、という理由です。

例えば、Web側のテストランナーとして使っている Testem ですが、
CI 用の出力モードを持っており、CUIのテストとして使えないこともありません。

しかし、実行が遅い、console.log が出力されない、などの不便な点が多く、
メインのテスト実行方法として使うには適していませんでした。

今回、RequireJS を使いつつ以下のテスト方法が揃ったので、

  • mocha コマンドからバシバシ CUI でテスト
  • たまに testem ci で、ブラウザでもエラーになってないかをテスト
  • 表示やアニメが絡むものは testem + ブラウザを実操作でテスト

状況により使い分けることで、開発がやりやすくなったと思います!

参考リンク