The little book of Buster.JS¶
Buster.JSについての小さな電子書籍
Contents:
Buster.JS のインストール¶
VMにインストール¶
VirtualBoxを使って Buster.JS をインストールする。 この方法ならWindowsであっても同様の環境を構築できる
try-busterjs¶
VirtualBox を使うためホストOSに関係なく、 同じ方法を使ってVirtualBox上で動く Buster.JS 環境を構築できます。 あくまで、用意されたお試しの環境をVirtualBoxに作るものなので、ちゃんとやりたい場合は 自分で同じように作ったほうがいいかもしれません。
Note
今回はホストOSがWindowsを例にする
まずは、VirtualBox - Download と Vagrant - Downloads から それぞれのインストーラーをダウンロードしてインストールします。
Vagrant は任意のディレクトリにインストールし、コマンドラインから利用するためにPATHを通す必要があります。 今回は D:\ruby\vagrant\ にインストールし、 D:\ruby\vagrant\bin へPATHを通すようにしました。
Ruby + VirtualBox + Vagrant が揃ったら、まずはvagrantで利用できるVMイメージをbaseという名前で取得する
$ vagrant box add base http://files.vagrantup.com/lucid32.box
次に try-busterjs のリポジトリをcloneしてVagrantのセットアップを行います。
$ git clone https://github.com/mroderick/try-busterjs.git
$ cd try-busterjs
$ vagrant up
vagrant up するとVirtualBox上でUbuntuが立ち上がるので、 このゲストOSに対してSSHで接続して Buster.JS を動かす事ができます。
MacやLinuxではsshがコマンドラインから利用できるので、 vagrant ssh を行うだけでSSH接続できますが、 Windowsでは別途 PuTTY や RLogin といったSSHクライアントとして 動作するソフトウェアを使用してSSH接続を行います。 [1]
Note
今回は RLogin を使用
SSHクライアントでの接続方法は vagrant ssh を行うと表示されますが、
HOST : 127.0.0.1
Username : vagrant
Password : vagrant
Private key : ~/.vagrant.d/insecure_private_key
Port : 2222
上記のような設定で、SSHの鍵はユーザーのホームディレクトリ以下に /.vagrant.d/insecure_private_key と いうものができているため、それを指定します。

SSH接続ができたら、後は通常のLinuxでの Buster.JS 環境と同様になります buster コマンドがインストールされているかを確認して、もしインストールされてないならnpmでインストールしておきます。
$ sudo npm install -g buster
# パスワードは vagrant
これで、 try-busterjs を使ってVirtualbox上に Buster.JS 環境を構築できましたが、 もっと詳細に設定等をしたい場合はSSHで利用するLinux on VM [2] などを作成するのがよいと思われます。
ホストOS:Windows、ゲストOS:Ubuntuとして、VBoxHeadlessTray などを利用して、 ヘッドレスでVM上にLinuxを動作させて、SSH接続して利用すれば、普通の利用の範囲なら速度やメモリ消費量的にも問題無い程度で動作させることができると思われます。
[1] | Get Started With Vagrant On Windows — zamboni 0.8 documentation |
[2] | WindowsからVM上のLinuxをSSH経由で利用する開発環境の構築 | Web scratch |
簡単なテストを動かしてみよう¶
設定ファイル-buster.js¶
Buster.JSではコンフィグファイルを作成し、そこへテストで使用するソースファイルやテストファイルの読み込みの定義や ブラウザ、Node環境どちらで実行するテストなのかなどを指定する必要があります。
デフォルトでは buster.js というファイル名のjsファイルが設定ファイルとして利用されます。
以下のような Buster-Project というディレクトリ直下に buster.js(設定ファイル) が有る場合はそれが利用され、 spec または test ディレクトリに buster.js(設定ファイル) が置かれている場合はそちらが利用されます。
Buster-Project
├── buster.js
├── spec/
│ └── buster.js
└── test/
└── buster.js
通常はtestかspec ディレクトリに置いておくのが良いですが、任意のディレクトリにおいて buster test の --config オプションで任意の場所にある設定ファイルのパスを指定して利用できます。
$ buster test
// test/buster.js or spec/buster.js
$ buster test --config path/to/buster.js
// 任意の場所の設定ファイルを指定できる
シンプルなNodeテストを動かしてみよう¶
使用するサンプルは以下のリポジトリにまとめられているので git clone するなどして取得して下さい
- azu/busterjs-kumite
- git clone https://github.com/azu/busterjs-kumite.git
See also
- busterjs-kumite/getting-started at master · azu/busterjs-kumite · GitHub
- cd `git rev-parse --show-toplevel`/getting-started
- codestre.am: streaming your code to the masses
- サンプルを実際に動かしてるコンソール動画
Note
上記の動画は codestre.am で録画したのでコピペもできます。
今回の動かすサンプルは getting-started/ ディレクトリにあります。 まずは動かしてみましょう。
Buster.JS のインストール を参考にBuster.JSがインストール済みならば、 次のように getting-started/ ディレクトリで buster test コマンドを叩くことで テストが実行されテスト結果が表示されます。
$ cd getting-started/
$ buster test
My Test Case: ....
1 test case, 4 tests, 4 assertions, 0 failures, 0 errors, 0 timeouts
getting-started/ ディレクトリ直下には buster.js(設定ファイル) が置かれているので、 今回は --config オプションで設定ファイルのパスを指定しなくても自動で読み込まれます。
./getting-started/
├── ./buster.js
└── ./test
└── ./test/simple-node-test.js
buster test によりテストが実行が確認出来たので、 buster.js(設定ファイル) の中身を見ていきます。
/busterjs-kumite/getting-started/buster.js
var config = module.exports;
config["My tests"] = {
env : "node", // or "browser"
tests : [
"test/*-test.js"
]
};
BusterJSでは JsTestDriver の jsTestDriver.conf のように 設定ファイルを読み込んでテストを実行しますが、 buster.js(設定ファイル) は名前の通りテスト設定もJavaScriptファイルで定義されます。
今回の設定ではnode環境で、testディレクトリにある *-test.js にマッチするテストファイルを読み込んで実行するという定義がされています。 testディレクトリ以下には simple-node-test.js しか無いので、そこに書かれているテストが実行されます。
/busterjs-kumite/getting-started/test/simple-node-test.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | var buster = require("buster");// for Node env
buster.testCase("My Test Case", {
"assert.euqals a === B" : function(){
assert.equals("foo", "foo");
},
"should be true" : function(){
assert(true);
},
"should be false" : function(){
refute(false);
},
"refute.euqals a !== B" : function(){
refute.equals("foo", "bar");
}
});
|
1行目は env : "node" の時に必要なだけなので、ブラウザテストの場合はなくても問題ありません。
buster.testCase 内で それぞれ以下のような形でテストケースを定義して使います。
"テスト名" : function(){
// テストの中身
}
Buster.JSのアサーション関数の多く [1] は assert/refute メソッドからなっています。
assert と refure は対になる関係で、 assertは assert(true); ならテストがパスされ、 refuteは refute(false); ならテストがパスされます。
Important
assert <-> refute
assert/refute はそれぞれ同様のメソッド(equalsやsame等)を持っているので、 この2つのアサーション関数を利用してテストを書いていくことになります。
[1] | BDD形式やモック関連等では異なる事がある |
テストの実行環境について¶
Buster.JS では大きく分けて、2つの実行環境でテストを実行できます。
- Node.js環境
- ブラウザ環境
簡単なテストを動かしてみよう ではNode.js環境でのテストを実行しました。
今回はそれぞれで実行する buster.js(設定ファイル) とテストの書き方と、両方で実行できるハイブリッドなテストファイルを見ていきます。
See also
- busterjs-kumite/config-patterns at develop · azu/busterjs-kumite · GitHub
- cd `git rev-parse --show-toplevel`/config-patterns
/busterjs-kumite/config-patterns/buster.js
var config = module.exports;
config["node tests"] = {
env : "node",
tests : [
"test/node-test.js",
"test/hybrid-test.js"
]
};
config["browser tests"] = {
env : "browser",
tests : [
"test/browser-test.js",
"test/hybrid-test.js"
]
};
Node環境の場合¶
上記の buster.js(設定ファイル) には、Nodeとブラウザの二種類のテスト設定が書かれています。 Buster.JSでは一つの buster.js(設定ファイル) に複数の設定ファイルを書くことができるようになっています。
Todo
詳細はconfigの項に書く
env : "node" と設定されているNode環境向けのテストを見ていきます。
テストを実行するには config-patterns ディレクトリにいる状態で、 buster test の -e/--environment オプションをnodeに指定して実行します。
$ cd config-patterns
$ buster-test -e node
type: .
Multi-environment: .
Skipping Multi-environment dependent on dom context, unsupported requirement: DOM
2 test cases, 2 tests, 2 assertions, 0 failures, 0 errors, 0 timeouts
-e/--environment オプションを指定すると、 buster.js(設定ファイル) で envが該当してるコンフィグのテストを実施します。 つまりこの場合は、 -e node と指定したので config[“node tests”] のテストが実行されます。
hybrid-test.js はとりあえず置いておいて、以下のテストファイルが読みこまれて実行されます。
/busterjs-kumite/config-patterns/test/node-test.js
var buster = require("buster");
buster.testCase("type", {
"isNull is null" : function(){
assert.isNull(null);
}
});
テスト自体は 簡単なテストを動かしてみよう と代わり映えしないので、次はブラウザ環境のテストを見ていきます。
ブラウザ環境の場合¶
Buster.JSでブラウザを使ったテストを行う場合は、Node環境のテストと違って少し準備が必要です。
大まかな手順を書くと以下のようになります。
- buster server を立ち上げる
- テストしたいブラウザで http://localhost:1111/capture へアクセスする
- buster test でテストを実行する
$ buster server
buster-server running on http://localhost:1111
$ open http://localhost:1111/capture # もしくは手動で開く
$ buster test -e browser # configがbrowserしかない場合は-eは指定しなくてもいい
実際に config-patterns ディレクトリのテストを動かしてみましょう。
$ buster server
$ open http://localhost:1111/capture
$ buster test -e browser
Chrome 19.0.1084.56, OS X 10.7 (Lion):
Firefox 13.0.1, OS X 10.7 (Lion):Ï
4 test cases, 6 tests, 8 assertions, 0 failures, 0 errors, 0 timeouts
ブラウザテストを実行すると、それぞれのブラウザで実行したテストの合計が表示されているので、 実際に書かれている テストケースの数 * ブラウザ数 の結果が表示されています。
config[“browser tests”] の中身を見ていきます。
/busterjs-kumite/config-patterns/test/browser-test.js
buster.testCase("DOM", {
"test exist DOM API" : function(){
var div = document.createElement("div");
assert.tagName(div, "div");// div or DIV
assert.tagName(div, "DIV");// 大文字小文字はどちらもいい
}
});
Node環境とは違い、最初に手動で var buster = require("buster"); する必要はありません。 また、実際にブラウザで動作するテストのため、DOM APIを使ったテストを実行する事ができます。
Buster.JSではassert.tagNameのようにDOMに関するassertionメソッドも用意されています。
ハイブリッドなテスト¶
最後にどちらの config でも読み込まれている、 hybrid-test.js について見ていきます。
このテストファイルでは、Node、ブラウザ どちらから読み込んでも動作し、また特定の環境でのみ評価されるようなテストも書かれています。
/busterjs-kumite/config-patterns/test/hybrid-test.js
if (typeof module == "object" && typeof require == "function"){
var buster = require("buster");// node環境なら読み込む
}
buster.testCase("Multi-environment", {
"runs in all environments" : function(){
assert(true);
},
"dependent on dom context" : {
// requiresSupportFor で trueの場合のみ、contextのテストが実行される
requiresSupportFor : { "DOM" : typeof document !== "undefined" },
"only runs in browser-like environments" : function(){
var el = document.createElement("p");
el.className = "item feed";
assert.className(el, "item");
}
}
});
requiresSupportFor に判定するためのオブジェクトを設定して、その中身がtrueなら そのコンテキスト(この場合は”dependent on dom context”の中)に書かれているテストが実行されます。
つまり、”dependent on dom context” の中に書かれているテストはNodeの場合documentオブジェクトがないため、評価されません。 実際に実行すると
$ buster test -e browser && buster test -e node
Firefox 13.0.1, OS X 10.7 (Lion):
2 test cases, 3 tests, 4 assertions, 0 failures, 0 errors, 0 timeouts
type: .
Multi-environment: .
Skipping Multi-environment dependent on dom context, unsupported requirement: DOM
2 test cases, 2 tests, 2 assertions, 0 failures, 0 errors, 0 timeouts
Nodeの方ではスキップされているため、結果に表示されるテスト数も異なる事が分かります。
このように、Buster.JSでは一つの buster.js(設定ファイル) に複数の設定を書いたり、 一つのテストファイルに複数の実行環境を場合分けして行うのを簡単にできるような仕組みが備わっています。
どの buster.js(設定ファイル) を使うかは buster-test の -c/--config オプションで指定できるので、 buster.js(設定ファイル) 自体を分割してやる方法でもいいと思います。
ブラウザ内でも使える機能が異なる事は多いため、 requiresSupportFor で実行されるブラウザのfeature detectをして、 実行されるテストを分けることでテストが複雑化し過ぎないようにする事もできます。
小さなJSのテストを書いてみよう¶
strftime¶
指定形式にフォーマットされた日時を取得できる関数 strftime を例にテストを書いてみます。
See also
- busterjs-kumite/strftime at develop · azu/busterjs-kumite · GitHub
- cd `git rev-parse --show-toplevel`/strftime/ 作成したサンプル
- Test-Driven JavaScript Development
- Buster.JSの作者が書いた書籍。strftimeをchapter1,2で扱ってる
- テスト駆動JavaScript
- 上記の書籍の日本語版
buster.js(設定ファイル) を作る¶
strftimeは特に環境固有の機能に依存しなくても実現できるため、 Node、ブラウザどちらの環境でも実行できるハイブリッドテストとして定義する。
/busterjs-kumite/strftime/buster.js
var config = module.exports;
config["My node test"] = {
env : "node", // or "browser"
tests : [
"test/*-test.js"
]
};
config["My browser test"] = {
env : "browser", // or "browser"
sources : [
"src/*.js"
],
tests : [
"test/*-test.js"
]
};
Nodeとブラウザ向けの設定をそれぞれ書いておくが、両者の違いとしては Node向けの設定ではsourcesの指定がなく、代わりにテストファイル自体に読み込むソースを定義している点が異なっている。
/busterjs-kumite/strftime/test/strftime-test.js
1 2 3 4 5 | if (typeof require == "function" && typeof module == "object"){
buster = require("buster");
// Nodeの場合はbuster.jsのsourcesでは読み込まれない
require("../src/strftime");
}
|
Node、ブラウザ環境 どちらで実行するかは テストの実行環境について で書かれているように、 buster test 実行時に -e/--environment オプション指定すればよい。
テストの実行¶
手動で実行する場合は、strftimeディレクトリ直下で buster test でテストを実行します。
$ buster test
毎回確認する度に、 buster test を実行するのは手間なので、ファイルの変更を監視して保存される度に buster test を実行してくれる buster autotest コマンドがあります。
$ buster-autotest
実装¶
strftime.jsの実装
/busterjs-kumite/strftime/src/strftime.js
// strftimeはDataオブジェクトを拡張する
if (typeof Date.prototype.strftime !== "function"){
Date.prototype.strftime = (function(){
function strftime(format){
var date = this;
return (format + "").replace(/%([a-zA-Z])/g, function(m, f){
var formatter = Date.formats && Date.formats[f];
if (typeof formatter == "function"){
return formatter.call(Date.formats, date);
}else if (typeof formatter == "string"){
return date.strftime(formatter);
}
return "%" + f;
});
}
// Internal helper
function zeroPad(num){
return (+num < 10 ? "0" : "") + num;
}
Date.formats = {
// Formatting methods
d : function(date){
return zeroPad(date.getDate());
},
m : function(date){
return zeroPad(date.getMonth() + 1);
},
y : function(date){
return zeroPad(date.getYear() % 100);
},
Y : function(date){
return date.getFullYear() + "";
},
j : function(date){
var jan1 = new Date(date.getFullYear(), 0, 1);
var diff = date.getTime() - jan1.getTime();
// 86400000 == 60 * 60 * 24 * 1000
return Math.ceil(diff / 86400000);
},
// Format shorthands
F : "%Y-%m-%d",
D : "%m/%d/%y"
};
return strftime;
}());
}
strftime-test.js の実装
/busterjs-kumite/strftime/test/strftime-test.js
// テストケース
buster.testCase("Date strftime tests", {
setUp : function(){
this.date = new Date(2009, 9, 2, 22, 14, 45);
},
"test format specifier %Y" : function(){
assert.same(Date.formats.Y(this.date), "2009",
"%Y should return full year");
},
"test format specifier %m" : function(){
assert.same(Date.formats.m(this.date), "10",
"%m should return month");
},
"test format specifier %d" : function(){
assert.same(Date.formats.d(this.date), "02",
"%d should return date");
},
"test format specifier %y" : function(){
assert.same(Date.formats.y(this.date), "09",
"%y should return year as two digits");
},
"test format shorthand %F" : function(){
assert.same(Date.formats.F, "%Y-%m-%d",
"%F should be shortcut for %Y-%m-%d");
}
});
それぞれのテストごとに、new Dateするのは冗長なので、 setUp で this.date = new Date(); のようにして行なっておけば、 それぞれのテスト実行前にsetUpが実行されて、 this.date で参照できるため共通部品がまとめられます。
assert.same は === の厳密比較演算子での比較結果がtrueならテストがPassされます。
つまり、イメージ的には以下のような感じです。
// assert.same(Date.formats.Y(this.date), "2009");
// ==>
if(Date.formats.Y(this.date) === "2009"){
// pass test
}else{
// fail test
}
やや、おさらい的な内容でしたが、これで短めなstrftimeの実装は終わりです。 一般的なstrftimeにはもっとformatsがあるので、続けてそれを実装してみるのもいいでしょう。
See also
- tokuhirom/strftime-js
- テストも書かれてるstrftimeの実装
今回、どのような手順で実装したのかは、このアプリのコミットまとめておいたのでそちらで見られるようにしてあります。
簡単に手順を並べると
- buster.js(設定ファイル) を書く
- strftime.js と strftime-test.js を作成して配置
- strftime.js にインターフェイスだけ(中身は実装してない)を実装 (先にインターフェイスだけ作ってるのは、テストを書くときにエディタから補完が効くので形だけ作っておいてる)
- strftime-test.js にテストを実装
- テストを実行しながら、 strftime.js に中身を実装
というような手順で実装しましたが、これが正解だというものは無いと思うのでまずは書いてみるのがいいでしょう。
テストを補助する機能について¶
See also
- busterjs-kumite/test-patterns at develop · azu/busterjs-kumite · GitHub
- cd `git rev-parse --show-toplevel`/test-patterns/ サンプルコード
setup/teardown¶
setUp/tearDownはそれぞれのテストの 実行前(setup)/実行後(teardown) に実行する関数を登録できます。 多くのテストフレームワークで同様の機能が提供されています。
/busterjs-kumite/test-patterns/test/setup-teardown.js
buster.testCase("setup/teardown", {
setUp : function(){
this.i = 1
},
tearDown : function(){
delete this.i;
},
"one" : function(){
this.i = 10;
assert.equals(this.i, 10);
},
"two" : function(){
assert.equals(this.i, 1);
}
});
上記の例では、 "one" と "two" それぞれの前後でsetUp/tearDownが実行されています。
テスト内で共通のプロパティを使いたい場合は、 this にプロパティを追加して利用できます。 ここでは、 this.i を実行前に値を設定して、実行後に削除しています。 そのため、 "one" の中で this.i の値を変更しても、 "two" には影響がでないようになっています。
Test case contexts¶
Buster.JSでは一つのtestCase内にコンテキストを複数持つことができます。
/busterjs-kumite/test-patterns/test/testcase-context.js
buster.testCase("testCase Context", {
"test #1" : function(){
assert.same("test", "test");
},
"context" : {
"test #2" : function(){
assert(true);
},
"Nest Context" : {
"test #3" : function(){
refute(false);
}
}
}
});
上記のコードだと、test #1が所属するコンテキスト、test #2が所属するのコンテキスト、test #3が所属するの3つがあることが分かります。 コンテキストにまとめることで、一つのtestCase内でもテストを一定のグループにまとめることができるようになっています。
コンテキストにはそれぞれ、setUp/tearDownを書くことができ、そこで書いたsetUp/tearDownはそのコンテキスト以下のテスト時に使われます。
また、 Test reporters によってはコンテキストを認識して見やすい形で出力してくれる場合もあります。
// Deferred tests¶
Deferred testsとはDeferredを使ったテストという意味ではなくて、そのテストそのものを延期することを示しています。 延期されたテストは、テスト実行時に実行されない用になります。
テスト名を//から始める事でそのテストは Deferred test になります。
/busterjs-kumite/test-patterns/test/deferred-test.js
buster.testCase("Deferred", {
"// test isn't implement" : function(){
},
context : {
setUp : function(){
buster.log("doesn't call");
},
"// this context all Deferred" : function(){
}
}
});
実行するとDeferred された有無も表示されます。 コンソールの場合は、 Deferred - Travis CI のような感じで出力されます。
また、 Test case contexts などで、そのコンテキストにDeferred testsのみしかない場合は、 そのコンテキストに存在するsetUp/tearDownは実行されないようになっています。
そのため、上記の例では buster.log("doesn't call"); は呼ばれない事になります。
非同期テスト¶
Buster.JSでは非同期のテストもサポートされている。 非同期テストの手法は大きく分けて3種類用意されている
- Callback done flag - 引数doneの実行を終了フラグとする
- Promise - Promiseオブジェクトをreturnする
- 非同期をMock/Stub/Fakeなどを使い同期的にする
Callback done flag¶
まずは、一番最初のケースについて。
下記のように、通常のテストにsetTimeout(非同期の動作)を混ぜてしまうとがsetTimeoutの中身を実行する前にテストが終了してしまい、 assertが一つもないテストという扱いになってしまいます。
"test not asynchronous" : function(){
setTimeout(function(){
assert(true);
}, 100);
}
// ✖ My thing test not asynchronous
// No assertions!
これを回避するためには、テストに指定する関数に引数を受け取るようにします。
その引数のコールバック(下記ではdone)が実行されるまで、テストは終了されないようになるため、非同期の動作を含んだテストを実行することができます。
/busterjs-kumite/test-patterns/test/async-test.js
buster.testCase("asynchronous", {
"test asynchronous" : function(done){
setTimeout(function(){
assert(true);
done();// test end
}, 100);
}
});
Promise¶
Promiseとは何かについては下記を参照して下さい。
- Promises/A - CommonJS Spec Wiki
- “promise” による JavaScript での非同期プログラミング - Internet Explorer ブログ (日本語版) - Site Home - MSDN Blogs
CommonJSのPromiseに沿ったシンプルなオレオレDeferred書いた - Cheese Pie より:
Promiseは"未完"な状態から始まり、"未完"あるいは"完了"あるいは"失敗"の状態へ遷移すること
Promiseは"then"という名前の関数をもったオブジェクトを返すこと
"then"メソッドはPromiseを返しチェインできるようにし、またコールバックでエラーが起きた場合は"失敗"の状態へ遷移すること
Buster.JSではPromiseを使ったテストをサポートしていて、テストでpromiseオブジェクトを返すと、 そのpromiseがresolveするまでテストの終了を待ってくれます。
これは Callback done flag での引数のコールバックdoneが実行されるまでテストが終了されないのと似ています。
実際のPromiseを使った例を見てみましょう。
Promiseは then メソッドを持ったオブジェクトであればいいので、特別なライブラリを使わなくても手動で実現することもできます。 下記の例では手動で実現するパターンと、Buster.JSに含まれているPromiseの実装である、 when.js を 使った2つの例が入っています。
/busterjs-kumite/test-patterns/test/promise-test.js
buster.testCase("Promise", {
"test async, use promise" : function(){
var promise = { // then メソッドの有無が重要
then : function(callback){
this.callbacks = this.callbacks || [];
this.callbacks.push(callback);
}
};
setTimeout(function(){
buster.assert(true);
var callbacks = promise.callbacks || [];
for (var i = 0, l = callbacks.length; i < l; ++i){
callbacks[i]();
}
}, 100);
// thenを持ったものを返す => promiseと判断 (isPromise)
return promise;
},
"test async ,use when.js" : function(){
var deferred = when.defer();
setTimeout(function(){
buster.assert(true);
deferred.resolver.resolve();
}, 100);
return deferred.promise;
}
});
どちらも、基本的に同じ事をやっていて、テスト関数内でreturnで then メソッドを持ったPromiseオブジェクトを返してあげる。 そのPromiseオブジェクトのresolveをする前に、テストしたい内容をassertしてあげて、resolveするとテストが終了します。
基本的にはwhen.jsを使った方がシンプルな記述で済みます。
Mock/Stub/Spy/Fake¶
Mock/Stub/Fakeを使って非同期のコードを同期的にテストしたり、 依存関係などを緩和してテストを実行したり、テストを楽にしてくれます。
Sinon.JS¶
Todo
@ryuoneさん、@kyo_ago さんがもっとくわしく書いてくれる
Buster.JSには Sinon.JS が統合されています。 Sinon.JS はをMock/Stub/Spy/Fake Timer/Fake XHRなどのテストを補助する機能が含まれています。
以下に、Sinon.JSの主要な機能を簡単に使ったテストを書いてみます。
/busterjs-kumite/test-patterns/test/sinon-test.js
buster.testCase("SinonJS", {
setUp : function(){
this.myLib = {
method : function(str){
return str;
}
}
},
"test a spy" : function(){
var spy = this.spy(this.myLib, "method");// sinon.spy
this.myLib.method("test");
assert.calledOnce(spy);
},
"test a stub" : function(){
var stub = this.stub(this.myLib, "method");// sinon.stub
stub.withArgs("hello").returns("world");// "hello" を渡すと "world" を返す
assert.equals(this.myLib.method("hello"), "world");// return "world"
},
"test a mock" : function(){
var mock = this.mock(this.myLib);// sinon.stub
mock.expects("method").withArgs("hello");// 期待値を設定
this.myLib.method("hello");
assert(mock);
},
"test async in sandbox" : {
setUp : function(){
// SandboxにFakeを作る
this.sandbox.useFakeServer();
this.sandbox.useFakeTimers();
},
tearDown : function(){
// Sandboxを戻す
this.sandbox.restore();
},
"test FakeTimer" : function(){
var spy = this.spy();
setTimeout(function(){
spy();
}, 16);
this.sandbox.clock.tick(16);
assert.called(spy);
},
"test FakeServer" : function(){
var expectRes = {
status : "OK"
};
var xhr = new XMLHttpRequest();
xhr.open('GET', '/get', true);
xhr.onreadystatechange = function(){
if (this.readyState === XMLHttpRequest.DONE && this.status === 200){
var res = JSON.parse(this.responseText);
assert.match(res, expectRes);
}
};
xhr.send();
this.sandbox.server.requests[0].respond(200, {}, JSON.stringify(expectRes));
}
}
});
コードを見てみると、 Sinon.JSを読みこまなくても this.spy,this.mock,this.sandbox などで参照できていることが分かります。 また、 assert.calledOnce のように、いくつかSinon.JSを前提とされているようなassertionが存在してることが分かります。
このように、Buster.JSとSinon.JSでは作者が同じ事もあって、Sinon.JSを使ったテストが多少楽に出来るようになっています。
Test reporters¶
Buster.js のテスト結果を出力する方法を決めるreportersという機能がある
reportersの種類¶
現在利用できるreportersの種類は下記に書かれているものと、独自に定義したreporterを利用できます。
デフォルトでは、 dots が使用され、buster-test の -r/--reporter オプションで指定することができます。 -r/--reporter オプションの詳細は、コマンドライン上で下記のようにして見ることができます。
$ buster test -h reporters
独自に定義したreporterを使う¶
reporter機能は自分で作成したものを利用する事ができます。
See also
- busterjs-kumite/reporters at develop · azu/busterjs-kumite · GitHub
- cd `git rev-parse --show-toplevel`/reporters
まずは、custom reporterを作成します。
/busterjs-kumite/reporters/reporter/myReporter.js
var terminal = require("buster-terminal");
module.exports = {
create : function(opt){
var reporter = buster.create(this);
opt = opt || {};
reporter.io = opt.io || require("util");
reporter.term = terminal.create(opt);
reporter.testCount = 0;
reporter.contexts = [];
return reporter;
},
listen : function(runner){
runner.bind(this, {
"suite:end" : "suiteEnd", "context:start" : "contextStart",
"context:end" : "contextEnd", "test:success" : "testSuccess",
"test:start" : "testStart", "test:deferred" : "testDeferred",
"test:failure" : "testFailure", "test:error" : "testEnd",
"test:timeout" : "testEnd", "context:unsupported" : "contextUnsupported"
});
return this;
},
suiteEnd : function(){
this.io.puts("1.." + this.testCount);
},
contextStart : function(context){
if (this.contexts.length == 0){
this.testCount = 0;
this.contexts.push(context.name);
this.io.puts("Browser : " + context.name);
}else{
this.contexts.push(context.name);
var space = new Array(this.contexts.length);
var color = "yellow";
this.io.puts(space.join("\t") + this.term[color]("<" + context.name + ">"));
}
},
contextEnd : function(){
this.contexts.pop();
},
contextUnsupported : function(data){
var name = data.context.name;
var features = data.unsupported;
var plural = features.length > 1 ? "s" : "";
this.testCount += 1;
this.io.puts("not ok " + this.testCount + " " + name + " # SKIP " +
"Unsupported requirement" + plural + ": " + features.join(", "));
},
testStart : function(test){
this.testCount += 1;
var space = new Array(this.contexts.length + 1);
this.spacer = space.join("\t");
this.test = test;
},
testSuccess : function(test){
this.test.passed = true;
this.testEnd(test);
},
testDeferred : function(test){
this.test.deferred = true;
this.test.comment = test.comment;
this.testEnd(test);
},
testEnd : function(test){
this.io.puts(this.spacer + label(this.test) + this.testCount + " " +
this.test.name);
},
testFailure : function(test){
this.io.puts(test.error.stack);
}
};
function label(test){
var label = "";
if (test.deferred){
label = "✎ ";
}else if (test.passed){
label = "✔ ";
}else{
label = "✘ ";
}
return label;
}
module.exports["test:error"] = module.exports.testEnd;
module.exports["test:timeout"] = module.exports.testEnd;
とりあえず、このcustom reporter(myReporter)を使ってテストを実行してみます。
buster server を行なって、ブラウザをキャプチャさせた状態で、 busterjs-kumite/reporters に、test.sh というファイルが用意されているのでそれを実行します。
Note
Windowsはshの方法は実行できないかも
$ sh test.sh
Browser : Firefox 13.0.1, OS X 10.7 (Lion)
<My Test Case>
<Context>
<Context NEST>
✎ 1 TEST Three
✔ 2 TEST TWO
✔ 3 TEST ONE
Browser : Chrome 20.0.1132.57, OS X 10.7 (Lion)
<My Test Case>
<Context>
<Context NEST>
✎ 1 TEST Three
✔ 2 TEST TWO
✔ 3 TEST ONE

これのように、自作のmyReporterを使ったテスト結果を実行するには、 -r/--reporter オプションでmyReporterと指定する必要がありますが、 適当な場所に作っただけではnodeの require() からは読み込めないため、 NODE_PATH に myReporter.jsがあるディレクトリを追加して、 require("myReporter") で読み込めるような状態で buster test -r myReporter して実行します。
つまり、以下のように NODE_PATH を設定してテストを実行させています。
NODE_PATH=reporter/ buster test -r myReporter
Buster.JSの仕組み的に、 buster-test / reporters.js にて、 -r/--reporter オプションで指定したモジュール(又は BUSTER_REPORTER の環境変数でも指定可能) を読み込む処理が行われています。
この読み込む処理は、単純に require("reporter名") しているため、NODE_PATHを使ったモジュールへのパスの通し方以外でも、 buster test 実行前にrequireで読み込めるように準備しておけば良いことになります。
custom reporterの書き方の詳細はドキュメントや既存のreporterが参考になります。
reporterに色を付ける¶
既存はspecification等のreporterには色が付けて出力されますが、 このようなターミナル上の色を付けるのには buster-terminal が利用できます。

taps reporterを参考に見ていくと、最初に var terminal = require("buster-terminal"); というように buster-terminal モジュールを読み込んでいます。
/busterjs-kumite/reporters/reporter/taps.js
var buster = require("buster-core");
var terminal = require("buster-terminal");
module.exports = {
create: function (opt) {
var reporter = buster.create(this);
opt = opt || {};
reporter.io = opt.io || require("util");
reporter.term = terminal.create(opt);
reporter.testCount = 0;
reporter.contexts = [];
return reporter;
},
このように、読み込むにはbuster-terminalモジュールへのパスが通ってないといけないので、 package.jsのdependenciesにモジュールを追加して置くのが楽でいいです。
相対パスしていでも読み込めるので該当するパスにファイルを置く方法でもできなくはないです。
"dependencies": {
"buster-core": ">=0.6.2",
"buster-terminal": ">=0.4.1",
}
使い方として、reporterのcreateで terminal.create(opt); して返って来たインスタンスを使います。
buster-terminal/lib/buster-terminal.js を 見ると、
term["green"]("色をつけたい文字列")
などのようにANSIカラーをつけたり、文字の一度調整等ができる関数が入ってるようです。
このように、Buster.JSのreporterはJavaScriptで比較的簡単にいじれるので、オレオレreporterを作って見るのもいいかもしれません。
テストとデバッグ¶
Buster.JSでテストを行いつつデバッグする方法
コンソールログと失敗したテストの見方について
コンソールにログを出力する¶
コンソールへのログ出力には buster.log(message) を利用できます。 これは buster.console.log() と同等で、buster.consoleにはよく見かけるコンソールAPIが存在しています。
buster.log(buster.console);
// => コンソールにフォーマットされて出力される
[LOG] {
contexts: { log: [undefined] },
debug: function () {},
error: function () {},
format: function () {},
level: "debug",
levels: ["error", "warn", "log", "debug"],
listeners: { log: [function () {}] },
log: function () {},
logFunctions: true,
warn: function () {}
}
buster.log(message) による出力は formatio により整形されて出力されます。 そのため、オブジェクトならオブジェクトをある程度展開した状態で出力されるようになっています。
DOMオブジェクトも同様に、HTMLタグの状態に展開されて出力されるようになっています。
var p = document.createElement("p");
p.id = "sample";
p.className = "notice";
p.setAttribute("data-custom", "42");
p.innerHTML = "Hey there, here's some text for ya there buddy";
buster.log(p);
// => [LOG] <p id="sample" class="notice" data-custom="42">Hey there, here's so[...]</p>
// 20文字で短縮される
See also
- busterjs/formatio
- Pretty formatting モジュール
また、デフォルトではウェブで一般に使われるコンソールAPIの window.console は buster.console にバインディングされています。 つまり、 console.log などの基本的なコンソールAPIはそのまま使うことが可能です。
assert.same(console.log, buster.console.log);// pass
このバインディングを無効にするには、 buster test コマンドのオプションに -o, --release-console オプションを指定することで行えます。 (ブラウザ側のコンソールに出力されるようになります)
See also
- Test options — Buster.JS 0.6.2 documentation
- buster-test option documents
失敗したテストを眺める¶
See also
- busterjs-kumite/debug at develop · azu/busterjs-kumite · GitHub
- cd `git rev-parse --show-toplevel`/debug
上記のサンプルで buster.js(設定ファイル) のconfigグループの”Failure tests” を指定して実行する事で見られます。 -g/–config-group オプションを指定することで特定のconfigグループを実行できます
buster test -g "Failure tests"
大きく分けてテストの失敗には3つぐらいあるので、それを見ていきます。
/busterjs-kumite/debug/test/failure-test.js
if (typeof module == "object" && typeof require == "function"){
var buster = require("buster");// node環境なら読み込む
}
buster.testCase("My Failure Test", {
"Assertion failure" : {
"refute should fail" : function(){
refute(true);
// => [refute] Expected true to be falsy
},
"assert.same should fail" : function(){
assert.same("A"); // 本来は比較対象の第二引数が必要
// => [assert.same] Expected to receive at least 2 arguments
}
},
"Async failure" : {
"doesn't call assert" : function(){
setTimeout(function(){
assert(true);// 非同期なので呼ばれない
}, 100);
// => No assertions!
}
},
"Exception failure" : {
"exception test" : function(){
throw new Error("cause error");
assert(true);
} // => Error
}
});
実行結果 :

Assertion failure¶
これが一番遭遇しやすいと思いますが、 buster-assertions のassertメソッドでテストを行い、 その結果がfailとなる場合の事です。
テストに失敗すると言った場合は大体これを示す事になると思います。
上記の例ではassertの結果がfailの場合と、assertの使いかたが間違ってる場合の2種類載せています。 どちらも、何故failになったのかのメッセージが表示されています。
Async failure¶
Async failure としましたが、実行してみるとわかるように No assertions! となり、 一つもassertが実行されなかったためにfailとなっていることが分かります。
多くの場合は非同期なメソッドのコールバックでassertを行う場合に発生すると思います。
解決方法としては、 非同期テスト で紹介されている、非同期なメソッドをテストする仕組みを利用するか、 そもそもまだテストケースとして完成していないなら、 // Deferred tests としておくべきです。
Exception failure¶
これは名前の通り、テスト内でJavaScriptの例外が発生してしまったため、Errorとなっています。 表示も Failure ではなく Error となっています。
もし、例外が発生したかをテストしたいならば、 assert.exception() 等の 例外をキャッチしてテストするassertionを利用しましょう。
Error.stackの情報¶
テストが失敗した結果にはメッセージ以外にもJavaScriptのスタックトレースが表示されます。 ブラウザでJavaScriptのスタックトレースを見た事がある方は分かると思いますが、 スタックトレースはJavaScriptエンジンによって形式が異なります。
- occ/TraceKit · GitHub
- strawman:error_stack [ES Wiki]
- Latest topics > JavaScriptのスタックトレース - outsider reflex
See also
- testacular
- testacular は WebStormからtestacularでテストとデバッグをする方法 のような連携をするために、 スタックトレースの内容を整形する ErrorFormatter の仕組みが入っています
本来なら、スタックトレースにはテストファイル以外にも Buster.JS 自体に関するトレース等も出るはずですが、 デフォルトでは (./test/failure-test.js:15:20) といったようなテストファイルに関係する部分しか出ていないことが分かります。
Failure: Chrome Failure Test Assertion failure assert.same should fail
[assert.same] Expected to receive at least 2 arguments
at Object.buster.testCase.Assertion failure.assert.same should fail (./test/failure-test.js:15:20)
Buster.JS には Stack filter というモジュールがあり、 デフォルトで入っている dots や specification といった Test reporters ではそれによりフィルターされるようになっています。 (testacularのような整形までの仕組みは今のところ入っていませんが、 Test reporters 等でも実現できるとは思います)
このフィルターを外すオプションもあり、 f/--full-stacks オプションを指定することでフィルターなしの結果が出力されるようになります。 (通常はあんまり出す必要は無い気もしますが)
buster test -e browser -g "Failure tests" -f
// =>
Failure: Chrome My Failure Test Assertion failure assert.same should fail
[assert.same] Expected to receive at least 2 arguments
at Object.ba.fail (./buster/bundle-0.6.js:1483:25)
at assertEnoughArguments (./buster/bundle-0.6.js:1252:16)
at Function.ba.(anonymous function).(anonymous function) [as same] (./buster/bundle-0.6.js:1264:18)
at Object.buster.testCase.Assertion failure.assert.same should fail (./test/failure-test.js:15:20)
at asyncFunction (./buster/bundle-0.6.js:6640:19)
at callTestFn (./buster/bundle-0.6.js:6746:27)
at ./buster/bundle-0.6.js:790:27
at ./buster/bundle-0.6.js:790:27
at p.then (./buster/bundle-0.6.js:71:31)
at Object.then (./buster/bundle-0.6.js:177:11)
Todo
-f/–full-stacks や スタックトレースのドキュメントリンクを追加する(まだない)
以上で、 buster.log でのログ出力やテストの失敗するケースやエラースタックの内容について簡単に紹介しました。
WebStormのデバッガー連携¶
Buster.JS はテストの実行環境として buster server と buster static のコマンドがあります。 buster server とは違い buster static はウェブページとして静的なテストページを用意され、 ブラウザでそのURLにアクセスするとQUnitなどのようにテストが実行されます。
つまり、 buster static を使った場合は通常のウェブサイトをデバッグする方法が利用できるため、 ブラウザのデバッガーを使ってテストケースのデバッグなども行いやすいです。
それを利用して、 WebStorm のJavaScriptデバッガーを使って、 WebStormからtestacularでテストとデバッグをする方法 のようにBuster.JSのデバッグを行うことも可能です。
See also
- WebStormのデバッガでBuster.JSのテストをデバッグをする方法 | Web scratch
- WebStormのJavaScriptデバッガーを使ってデバッグする方法について
エディタ対応¶
Buster.JSのエディタ対応について
WebStorm¶
WebStorm では自分が公開してるBuster.JSについてのJSDocファイルを利用できます。
WebStormの Settings ‣ JavaScript ‣ Libraries に下記のjsファイルを追加する事で、 BusterJSのメソッドを補完候補に加えたり、IDE上から簡単なドキュメント(Quick Lookup)を参照できます。
Todo
ConfigurationやExternal Toolsとの連携方法についても書く
Travis CIでブラウザテスト¶
Travis CI はGithubアカウントを使ってログインして利用するCIサービスで、CIしたいプロジェクトを選択すればGithubへpushにhookしてテストが実行されます。 実行するテストの設定ファイルを .travis.yml に書いて置くことでどのようなテストを実行するかを設定できます。
テストが失敗したり、失敗してたテストが直った場合はメールで通知などを飛ばすこともできます。 また、テストの成否はコマンドの終了ステータスで行われていて、 0 なら成功、それ以外だと失敗というステータスになります。 大抵のテストフレームワーク(or 実行環境)などはちゃんと終了ステータスを返してくれるのでテストの成否は正しく判定できます。
こういうウェブサービスの場合、DOMやXHRなどがないJavaScriptのロジックテストのみしか動かせないように思われてしまいますが、 Travis CI はFirefoxやPhantomJS(Webkit)を使って実際のブラウザを動かしてテストを行う事ができます。
Buster.JS ではブラウザをキャプチャページにアクセスさせればブラウザ上でテストが実行できるため、 .travis.yml にかかれたコマンドのみでテストを走らせられるので Travis CI でもBuster.JSのブラウザテストが行えます。
実際に Travis CI で動くサンプルプロジェクトを作ってみましょう。
npm package.json¶
まず、Travis CI にはBuster.JSは入ってないので、テストを走らせる前にそれらをTravis CIにインストールさせるようにします。
Buster.JSはnpmで配布されているので、テストの依存関係の設定をnpmの package.json にまとめて置くと、 npm install するだけで準備が出来るので、package.jsonを書いていきます。
また、 package.jsonを作っておくと、githubを見た人も npm install でテスト環境を作れるようになるので、 このように設定をまとめておくのは有用だと思います。
実際に azu/BusterJS-TravisCI に書かれている BusterJS_TravisCI/package.json を見てみます。
{
"author" : "azu",
"name" : "BusterJS_TravisCI",
"description" : "Buster.JS with Travis CI example",
"version" : "0.0.1",
"scripts" : {
"test" : "node_modules/.bin/buster-test"
},
"dependencies" : {
"buster" : "~0.5.0"
},
"engines" : {
"node" : "~0.6"
}
}
dependencies を見るとbusterの”~0.5.0”に依存していると書かれています。 “~0.5.0”とは0.5.0以上で0.6.0未満で一番バージョン番号が新しいものがインストールされるという意味になります。 同じように engines でnodeのバージョン番号を指定しています。
scripts では npm test とコマンドを叩いた時に実行される動作を指定できます。 ここでは npm install でインストールしたnode_modules/のbuster-testコマンドを実行するようにしています。
npm test については以下が参考になります。
この状態で、 npm install してから npm test すると、同じディレクトリにある buster.js の設定ファイルを元に Buster.JSのテストが走ることが確認できます。
他にもpackage.jsonでは色々設定できますが、package.jsonで設定できることを詳しく知りたい場合は以下を見るのがいいでしょう。
.travis.yml¶
.travis.yml では Travis CI がテストを実行する前に行うことや通知の設定や実行するテストコマンドなどを指定できます。
before_script:
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
- sleep 5
- node_modules/.bin/buster-server &
- sleep 5
- firefox http://localhost:1111/capture &
- sleep 5
- phantomjs node_modules/buster/script/phantom.js http://localhost:1111/capture &
- sleep 5
script:
- "npm test"
language: node_js
node_js:
- 0.6
テストの実行環境はnodeを使うため、languageにnode_jsとし、node_jsのバージョンも指定します。 scriptではテスト実行時に行うコマンドを指定できるので npm test とします。(無指定でもこれが使われる)
Buster.JSではテスト実行前にブラウザをキャプチャしておかないと行けないので、 before_script でscriptの行われる前に Buster.JSでテストを行う準備の設定を記述します。
Travis CIでのブラウザを使ったテストについては Travis CI: GUI & Headless browser testing on travis-ci.org にも 書かれていますが、GUIが必要な場合はxvfbを動かすようにします。
次に、 buster-server コマンドでBuster.JSのサーバをたちあげたら、 キャプチャするURLに対して、Travis CIに入ってるFirefoxとphantomJSを使ってそこへアクセスするようにします。
Buster.JSではPhantom.js用のスクリプトが buster/script/phantom.js に用意されてるので、それを利用してキャプチャURLにアクセスさせます。 最近のPhantom.jsではXvfbに依存しなくなったので、Phantom.jsだけを使う場合はxvfbはstartしなくてもいいかもしれません。
これで、Travis CI上で次のようにテストが走って失敗なら通知をおくってくれるようになるので、 Githubで公開してるJavaScriptプロジェクトのCIが簡単に行うことができるようになります。

サンプルプロジェクト
Travis CI ではビルドステータスの画像を取得するURLもあるので、Githubにreadmeなどに貼り付けておくと分かりやすい。

また、このドキュメントで使用されているコードも同様にBuster.JSとTravisCIを使いテストされています
他のCIサービスについて¶
Travis CI 以外にも Drone.io やJenkins系の BuildHive 等CIサービスは色々あります。
また、今回は Travis CI にデフォルトでインストールされてるFirefoxとPhantomJSでのみでテストを動かしましたが、 少し設定を加えればChromeも同様にテストを動かすことができます。
それらのことについてまとめた記事が以下で公開されています。
See also
- CI as a Service – ブラウザを使ったJavaScriptのテストをCIサービスで動かす方法のまとめ
- Travis CI , Drone.io , BuildHive , Jepso CI でテストを動かす方法についてまとめた記事
Contributionするには¶
このドキュメントはSphinxで書かれています。
ドキュメントのソースと使用しているコードはGithubで管理されています。
間違いの指摘や追加したい内容等はGithubにIssueやPull Requestを送って下さい。
以下はこのドキュメントを書く際の注意点のようなものです。
ドキュメントスタイルガイド¶
基本的なrst文章の書き方については以下が参考になります。
- Sphinxドキュメント — Sphinx documentation
- 逆引き辞典 :: ドキュメンテーションツール スフィンクス Sphinx-users.jp
- Documentation style guide — Style guide for Sphinx-based documentations 0.1dev documentation
- Sphinxでドキュメントを書くためreST記法に入門した - kk_Atakaの日記
- Sphinxを使ってプログラムドキュメントを楽しく書こう - maru source
用語の統一について¶
いくつか利用頻度の高い用語については、統一した記法を用います。
- Buster.JS
Buster.JS のテスティングフレームワーク自体を示す。 公式サイトの記法と同じ、 “Buster.JS” という表記に統一する
|Buster.JS| という置換記法を利用する事で用語集へのリンクをつけたものに置換される
- buster.js(設定ファイル)
Buster.JSで用いる buster.js というファイル名の設定ファイルの事。 “buster.js設定ファイル” 又は” buster.js 設定ファイル “という表記を使う
|buster.js| という置換記法を利用する事で用語集へのリンクをつけたものに置換される。 buster.js(設定ファイル)と毎回書くのは冗長なため |buster.js| と書くのを推奨する。
サンプルのコード表示について¶
azu/busterjs-kumite に置かれているサンプルコードを参照する場合は、 以下のような形で、サブモジュールにおいてあるコードを読み込んで利用する。
.. literalinclude:: /busterjs-kumite/getting-started/test/simple-node-test.js
:language: js
:linenos:
コードを読み込み表示する際に :linenos: で行番号を表示させる場合でも、 ファイルがいつ変更されるかわからないため、直接その行番号に対して言及するのはできるだけ避けるようにする。 1行目等、殆ど変わることがない場合はその限りではない。
直接コードを参照する場合、または azu/busterjs-kumite 以外のコードを参照する場合は コードディレクティブを用いて表示する。 その際に、シンタックスハイライトが出来る言語であるなら、正しく指定する
.. code-block:: js
var code = "サンプルコード";
Buster.JS ドキュメントへのリンクについて¶
Buster.JS のドキュメントに対してリンクを張る時に、 intersphinx が利用できる場合(ラベルが設定してある)は
:ref:`buster:buster-test-reporters`
のように、 refを使ったリンクにすることを推奨します。 Buster.JSのドキュメントのdomainには buster を割り当ててあるので、
:ref:`buster:ラベル名`
という感じで、Buster.JSのドキュメントに対して参照を貼ることができます。
- sphinx.ext.intersphinx – 他のプロジェクトのドキュメントへのリンク — Sphinx 1.1 (hg) documentation
- 2012/12/08 intersphinxを使おう - #sphinxjp アドベントカレンダー2012 - 清水川Web
ラベルが見つからない場合は、無理してやるよりかは通常のURLリンクで問題ありません。