Broccoliプラグインのテストをする方法として、ひとつは実際にbroccoli buildコマンドを実行し、その後出力されたファイルの内容に対しアサーションを行う、というものが考えられます。broccoli-mdなどはこの手法採っています。しかし、これには何かと問題があります。ビルドとテストを別プロセスで行うので、エラーが発生するケースのテストやカバレッジの計測などが困難です。

そこで、コマンドラインツールではなくbroccoli本体のAPIを直接呼び出してビルドを行う方法が役に立ちます。broccoli-traceurテストに使われている手法で、broccoli-clean-cssのテストにも採用しました。

Broccoli の API

Broccoliはビルダー、ファイルウォッチャーおよびサーバーとしての機能を持ちあわせていますが、ビルダーとしての機能のほとんどはBuilderクラスを定義するlib/builder.jsに書かれています。

内部の処理としては、broccoli buildコマンドが実行されると、

  1. Brocfile.jsがエクスポートするtreeオブジェクトを受け取り、
  2. それをBuilderクラスに引数として渡しインスタンスを作り、
  3. インスタンスのbuildメソッドを呼び出しビルドを開始する

という流れになっています。つまり、これらの手順をAPIを呼んで直接実行してしまえば、broccoli buildコマンドを実行しなくともスクリプトの中でビルドとテストをまとめて行えるのです。

以下のようなBrocfile.jsがあるとします。

'use strict';

var cleanCSS = require('broccoli-clean-css');

module.exports = cleanCSS('src');

src/style.cssのビルド結果をテストしたい場合は、以下のようなテストコードになります(テストフレームワークはtapeを使用しています)。

'use strict';

var fs = require('fs');

var Builder = require('broccoli').Builder;
var cleanCSS = require('broccoli-clean-css');
var test = require('tape');

test('Minify CSS', function(t) {
  var app = cleanCSS('src'); // 本来 Brocfile が export するファイルツリー
  var expected = 'b{color:red}'; // 期待されるビルド結果

  new Builder(cleanCSS('src')).build().then(function(dir) {
    fs.readFile(path.join(dir.directory, 'styles.css'), 'utf8', function(err, content) {
      t.strictEqual(err, null);
      t.equal(content, expected);
    });
  }, t.fail);
});

buildメソッドはPromiseを返します。ビルドが成功すれば、ファイルの出力先などの情報を含んだオブジェクトをハンドラに渡します。あとは出力済みのファイルの内容をfs.readFileなどで読み込み、期待される出力と比較するだけです。これで、Brocfile.jsとテストスクリプトを別々に用意せずに、単一ファイルでビルド + テストをこなすことができるようになりました。

sum-upの紹介記事の翻訳と、NodeのCLIユーティリティについて
モジュールの開発にJavaScriptトランスパイラを使わないという選択