CI as a Service ブラウザを使ったJavaScriptのテストをCIサービスで動かす方法のまとめ
CI as a Service
テスト実行の流れ
Jepso CI を除いては、テスト実行の流れ自体は同じなので先に解説します。 (一)Capture用のローカルサーバを立てる (二)テストしたいブラウザでcapture URL
へアクセスする
(三)コマンドラインからテストを実行する
(四)Captureされてるブラウザでテストを実行した結果が得られる
これはJsTestDriverの流れを組んでいるTest Runnerには大体共通してると思います。
この一連の流れをCIサービス上で実行して、Githubにpushするたびに自動でテストを行うようにするのが今回の目的です。
どのCIサービスも裏側ではUbuntu上で動いていて、apt-getやnpmを使ってテスト環境を事前に準備する必要があります。
また、Travis CIやdrone.ioには最初からNode.js環境なども用意されているので、BuildHiveに比べると事前の準備は簡潔に済みます。
Travis CI
Travis CIでは.travis.ymlという設定ファイルを書くことで、テスト環境を揃えることができます。
サンプルプロジェクトは以下においてあります。
●azu/Browser_CI_as_a_Service · GitHub
テスト環境のセットアップ
サンプルの Browser_CI_as_a_Service / .travis.yml を見ていきます。env:
- DISPLAY=:99.0
before_script:
- sh -e /etc/init.d/xvfb start
- sh .travis/scripts/setup_busterjs.sh
script:
- npm test
language: node_js
node_js:
- 0.8
Travis CIではデフォルトでNode.js(npm)、PhantomJS、Firefoxの環境が入っているため、before_scriptでインストールするものは、Buster.JSのみとなります。
Travis CIにデフォルトで入っているものは以下で見られます
.travis.yml内に language: node_js
を記述しておくと、自動で npm install
を実行してくれるので、
レポジトリの Browser_CI_as_a_Service / package.json の devDependencies
に Buster.JS を追加しておきます。
{
"author" : "azu",
"name" : "Browser_CI_as_a_Service",
"description" : "Buster.JS with CI Service example",
"version" : "0.0.1",
"scripts" : {
"test" : "node_modules/.bin/buster-test -r specification"
},
"devDependencies" : {
"buster" : "~0.6"
},
"engines" : {
"node" : "~0.8"
}
}
これで、自動で npm install
が実行されれば、テスト環境が揃います。
テストの実行を設定
.travis.yml の事前準備の部分を見ていきます
env:
- DISPLAY=:99.0
before_script:
- sh -e /etc/init.d/xvfb start
- sh -e .travis/scripts/setup_busterjs.sh
before_script:
で実行しているsetup_busterjs.sh は以下のような内容になってます。
#!/bin/sh
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
if [ -x "google-chrome" ]; then
google-chrome –no-default-browser-check –no-first-run –disable-default-apps http://localhost:1111/capture &
sleep 5
fi
script:
で npm test
を実行するようにしているので、npm test
でBuster.JSのテストが実行されるように package.json
に設定します。
(language: node_jsが設定されるなら省略可能です)
script:
- npm test
package.json
に以下のように書いて、npm test
でBuster.JSのテスト実行されるようにしました。
"scripts" : {
"test" : "node_modules/.bin/buster-test -r specification"
}
Travis CIの有効化
![Travis CI - Free Hosted Continuous Integration Platform for the Open Source Community 2013-03-20 17-31-36.jpg Travis CI Free Hosted Continuous Integration Platform for the Open Source Community 2013 03 20 17 31 36](https://efcl.info/wp-content/uploads/2013/03/repogitoriesTravis-CI-Free-Hosted-Continuous-Integration-Platform-for-the-Open-Source-Community-2013-03-20-17-31-36.jpg)
![Build Status](https://travis-ci.org/azu/Browser_CI_as_a_Service.png?branch=master)
drone.io
次は、 drone.io でのテスト環境を揃える方法についてです。
drone.ioではGithub/Bitbucket/Google Codeと連携できますが、今回は先ほどと同様のレポジトリを使うのでGithubの場合です。
また、drone.ioもNode.js環境が用意されていますが、PhantomJSとGoogle Chromeを追加でインストールして、
Firefox/PhantomJS/Google Chromeの3つでテストを動かしたいと思います。
![Admin azu 2013-03-20 18-43-45.jpg Admin azu 2013 03 20 18 43 45](https://efcl.info/wp-content/uploads/2013/03/repogitoriesAdmin-azu-2013-03-20-18-43-45.jpg)
drone.ioもTravis CIと同様にオープンなコードなら無料で殆ど利用できるようになっています。 (前月ぐらいまでは soft limitsな50回/monthの制限が書いてありましたがUnlimitedになってました)
テスト環境のセットアップ
drone.ioはTravis CIのように設定ファイル(.travis.yml)は用意する必要なく、直接ウェブでテスト環境を準備するスクリプトを書くようになっています。 なので、最初にGithubのレポジトリからdrone.ioにプロジェクトを作成します。 まず、 New Project から Githubを選択します。![Add Repo drone.io 2013-03-20 19-08-02.jpg Add Repo drone io 2013 03 20 19 08 02](https://efcl.info/wp-content/uploads/2013/03/repogitoriesAdd-Repo-drone.io-2013-03-20-19-08-02.jpg)
![Build Setup Browser_CI_as_a_Service 2013-03-20 19-42-58.jpg Build Setup Browser CI as a Service 2013 03 20 19 42 58](https://efcl.info/wp-content/uploads/2013/03/drone.io-setupBuild-Setup-Browser_CI_as_a_Service-2013-03-20-19-42-58.jpg)
sudo start xvfb
npm -d install
sh -e .travis/scripts/install_phantomjs.sh
sh -e .travis/scripts/install_chrome.sh
# start buster server
node_modules/.bin/buster-server &
sleep 5
# start browsers
firefox http://localhost:1111/capture &
sleep 5
phantomjs node_modules/buster/script/phantom.js http://localhost:1111/capture &
sleep 5
google-chrome --no-default-browser-check --no-first-run --disable-default-apps http://localhost:1111/capture &
npm test
npm install
でBuster.JSをインストールして、
PhantomJS(バイナリでインストールしたかったのでnpm経由)とGoogle Chrome(apt-getで取得してインストール)しています。
scriptはazu/Browser_CI_as_a_Service · GitHubにまとめてあります。
(jubianchi/travisci-helper · GitHub
こちらのスクリプトも参考になります)
後は、Travis CIと同じでbuster server
を起動して、ブラウザでCapture URLにアクセスして、
npm test
でテストを動かすという感じです。
Travis CIの setup_busterjs.sh を実行させようと思ったのですが、なぜかテストが成功しても終了しないという感じになったので直接書いています。
今回紹介したビルドスクリプトは Build Setup | Browser_CI_as_a_Service から参照できます。
![drone.io Build Status](https://drone.io/github.com/azu/Browser_CI_as_a_Service/status.png)
BuildHive
基本的にJenkinsの画面なので、人によっては馴染みやすいかもしれません。
BuildHiveもdrone.ioと同じく、ウェブ上でビルドスクリプトを書いてテスト環境を揃えます。
デフォルトでNode.js環境がないため、そこから環境を揃えていく必要があります。
ただし、Travis CIやdrone.ioと違って、一回ごとに仮想環境がリセットされないで継続する感じなので、少しセットアップ方法が変わります。
Add Git Repository からGithubのプロジェクトを選んで有効化します。
![BuildHive: Cloud Continuous Integration 2013-03-20 20-32-57.jpg BuildHive Cloud Continuous Integration 2013 03 20 20 32 57](https://efcl.info/wp-content/uploads/2013/03/drone.io-setupBuildHive-Cloud-Continuous-Integration-2013-03-20-20-32-57.jpg)
export PATH=$HOME/.nodebrew/current/bin:$PATH
if [ ! -x "$HOME/.nodebrew/current/bin/nodebrew" ]; then
curl https://raw.github.com/hokaccha/nodebrew/master/nodebrew | perl - setup
nodebrew install-binary stable
nodebrew use stable
fi
if [ -e /tmp/.X1-lock ]; then
rm /tmp/.X1-lock # たまにロックがかかって死ぬので
fi
if [ -z "$DISPLAY" ]; then
export DISPLAY=:1
Xvfb :1 &
fi
npm -d install
node_modules/.bin/buster-server &
sleep 5
firefox http://localhost:1111/capture &
sleep 5
node_modules/phantomjs/bin/phantomjs node_modules/buster/script/phantom.js http://localhost:1111/capture &
sleep 5
npm test
npm test
するという感じです。
Jepso CI
最後は Jepso CI についてです。
他のCIサービスが特にJavaScriptに限定されないのに対して、Jepso CIはHTMLとJavaScriptというものに限定される作りになっているので全く別の仕組みです。
Sauce や testling-ci の方に近いジャンルのCIサービスです。
また、まだ安定してるとは言えないので、実験的でテスト結果も安定しないのもあるため実用で使うのは諦めて下さい。
(2013-03-20)
サンプルプロジェクトはazu/BusterJS_JepsoCI · GitHubに置いてあります。
仕組み
他のCIサービスとは異なる仕組みで、基本的にローカルサーバなどは使わないで、 QUnitのようなテストを走らせるための静的なHTMLファイルと、そのファイルへのパスを書いた.jepso-ci.json
という設定ファイルだけです。
今回のプロジェクト(azu/BusterJS_JepsoCI · GitHub)ではルートに、テストを走らせるための test.
html
をおいてるので、
.jepso-ci.json
は以下のような内容が入ります。
{
"url": "/test.html"
}
次に、BusterJS_JepsoCI / test.html を見ていきます。
Jepso CIのテストの成否判定は、test.html内の2つのプロパティによって判定されます。
window.testsPassed;// bool
window.completedTests;// number
Buster.JSのtest runnerとなる test.html
は以下のような感じです。
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>Buster.JS</title>
<link rel="stylesheet" href="http://cdn.busterjs.org/releases/latest/buster-test.css">
<script type="text/javascript" src="http://cdn.busterjs.org/releases/latest/buster-test.js"></script>
<span class="c"><!-- jepso-ci --></span>
<span class="nt"><script </span><span class="na">type=</span><span class="s">"text/javascript"</span><span class="nt">></span>
<span class="c1">// start test</span>
<span class="nx">buster</span><span class="p">.</span><span class="nx">testRunner</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s2">"suite:start"</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">results</span><span class="p">)</span> <span class="p">{</span>
<span class="nb">window</span><span class="p">.</span><span class="nx">completedTests</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="nb">window</span><span class="p">.</span><span class="nx">testsPassed</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
<span class="p">});</span>
<span class="c1">// each passed test</span>
<span class="nx">buster</span><span class="p">.</span><span class="nx">assertions</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s2">"pass"</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="nb">window</span><span class="p">.</span><span class="nx">completedTests</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">});</span>
<span class="c1">// each failure test</span>
<span class="nx">buster</span><span class="p">.</span><span class="nx">assertions</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s2">"failure"</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span>
<span class="nb">window</span><span class="p">.</span><span class="nx">testsPassed</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="p">});</span>
<span class="c1">// finish all test</span>
<span class="nx">buster</span><span class="p">.</span><span class="nx">testRunner</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s2">"suite:end"</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">results</span><span class="p">)</span> <span class="p">{</span>
<span class="nb">window</span><span class="p">.</span><span class="nx">testsPassed</span> <span class="o">=</span> <span class="nx">results</span><span class="p">.</span><span class="nx">ok</span><span class="p">;</span><span class="c1">// result boolean</span>
<span class="p">});</span>
<span class="nt"></script></span>
<span class="c"><!--test source--></span>
<span class="nt"><script </span><span class="na">src=</span><span class="s">"./src/hello.js"</span> <span class="na">type=</span><span class="s">"text/javascript"</span><span class="nt">></script></span>
<span class="nt"><script </span><span class="na">src=</span><span class="s">"./tests/async-test.js"</span> <span class="na">type=</span><span class="s">"text/javascript"</span><span class="nt">></script></span>
<span class="nt"><script </span><span class="na">src=</span><span class="s">"./tests/hello-test.js"</span> <span class="na">type=</span><span class="s">"text/javascript"</span><span class="nt">></script></span>
</head>
<body>
</body>
</html>
window.t
estsPassed
が true
ならテストが通った事になります。
window.completedTests
は非同期のテストがある場合に、++していくとtimeoutを伸ばしてくれるためにあるようです。
実際の処理内容を見ていくと、Buster.JSは .on
でテスト実行時のイベントを設定できるので、以下のような処理をイベントに仕込んでいます。
buster.testRunner.on("suite:start", function… はテスト開始時に初期化
buster.assertions.on("pass", function... でテストが通るたびにインクリメント
buster.assertions.on("failure", function... で一つでも失敗したらテスト失敗
buster.testRunner.on("suite:end", function... でテスト終了時にテスト結果を代入
後は、テストファイルを読み込ん実行するだけです。
(単純に*.jsファイルをscriptタグで読み込んでる)
Jepso CIとGithub連携
Githubとの連携はPost-Receive Hooksの”WebHook URLs”にhttps://jepso-ci.c
om/api/hook
と設定するだけです。
後は、GithubにpushすればJepso CIが動作するようになります。
Jepso CIのページは https://jepso-ci.c
om/<user>/<repogitory>
にアクセスすると表示されます。
FAQ
Travis CIやBuildHiveではChromeは使えないの?
Travis CI がUbuntu-64bitに移行した際にChromeが追記 : Travis CIとChromeについて
記事ではChromeはインストールしていませんでしたが、以下のような変更を入れることで、 Travis CI上でもChromeを動作させることができます。 ●fix Google-Chrome for Travis CI · d736273 · azu/Browser_CI_as_a_Service ●修正したコミット ●Travis CI – Free Hosted Continuous Integration Platform for the Open Source Community ●実際に動いてる様子 ●Chromium/Chrome does not work in an OpenVZ container · Issue #938 · travis-ci/travis-ci 具体的には以下のようなことが必要になります。/dev/shm
のパーミッションを修正する
sudo chmod 1777 /dev/shm
- Chromeの起動引数に
--no-sandbox
をつける
--no-sa
ndbox
の副作用が何かあるかもしれませんが)
sudo
が使えなかったので、上手くインストール出来ませんでしたが、上手くやればインストールできるかもしれません
もっといろんなブラウザのバージョンで試したいんだけど?
今回紹介した Travis CI、drone.io、BuildHive はブラウザに特化した作りでは無いので、自前でバージョンごとのブラウザを用意すればできますが、負荷が大きそうなのでそういう用途は避けましょう。(また全体として成功か失敗かなのであんまりそういう用途ではない気がする) BrowserStack や testling-ci 、Sauce Labs などブラウザに特化したサービスを利用するか自前のサーバ等でやりましょう。CI Servicesの環境に何が入ってるかもっとしりたい
公式サイトにサーバのスペックは書いてあると思います。 デフォルトに入ってるものについてはTravis CIはtravis-ci/travis-cookbooks · GitHubを見ると大体わかります。 gildegoma/travis-ci-inspector · GitHubのように走査して何が入ってるか調べる地道な方法も利用できます。 (対話側のコンソールみたいなのが欲しいですね…)Example Project
今回使用したサンプルプロジェクトは以下のURLで公開しています。 ●azu/Browser_CI_as_a_Service · GitHub ●azu/BusterJS_JepsoCI · GitHubTest Tools
今回は Buster.JS を使用しましたが、Test Toolとしては他に以下のようなものもあります。Special Thanks
- ようせいさん(@kyo_ago)
お知らせ欄
JavaScript Primerの書籍版がAmazonで購入できます。
JavaScriptに関する最新情報は週一でJSer.infoを更新しています。
GitHub Sponsorsでの支援を募集しています。