NightmareJS+Dockerによる環境非依存なUIテストの導入
Alpacaで主にフロントエンドを担当している北山(@gamella, blog)です。
フロントエンドを開発していると、「ログインして、これをクリックしたら、この表示が行われていること」みたいなUIテストを環境非依存で簡単に行いたいと思うことがありますよね?僕はあります。
Alpacaでは開発にDockerを全面採用しているということもあり、最近ちょくちょく目にするNightmareJSをDocker上で動かして簡単にUIテストを導入できたので、その知見を共有したいとおもいます。
まず、どうしてDockerを利用したいかということですがAlpacaでは、すべての機能をDocker上で動作させているため、それに倣っているいうこともありますがUIテストをローカルでもCircleCIでも、Dockerが動作する環境であればどこでもコードの改変なしで実施できるというのは大きな魅力だと思います。特にAlpacaは以下のエントリーのようにネットワークも含めてすべてDocker経由でコントロールしているため、UIテスト用のDockerイメージもこのネットワークコンテナの一部としてdocker runすることで、外部サーバを利用せずに、Dockerコンテナ内で完結してテストを行うことができます。
UIテストにはSelenium WebDriverやPhantomJSを利用するのがメジャーですが、ここでNightmareJSを採用した理由としては、API設計が非常にモダンで使いやすいこと、またevaluate関数内でJQueryを利用して記述すれば、柔軟なUIテストが実施できることなどがあげられます。また、現在も活発に開発・高速化が続けられているElectronをベースに動作するため、ページ遷移や描画が高速に動作し、UIテストが可能でシンプルなテストケースであれば一ケースあたりおよそ1、2秒で実施することも可能です。
まず、NightmareJSをDocker上で動作させる情報としてはこのIssueが非常に役に立ちました。
このIssueの内容を咀嚼して実行すればほぼできるのですが、結構長いIssueですし簡潔に述べますと、ハマりどころはNightmareJSが現状Electronに依存しているので、Docker内でElectronが立ち上がるバーチャルなビデオ出力環境を用意して上げる必要がある点です。
僕はDockerfileを以下の内容にしています。 apt-getインストールしているのは、Electronの仮想ディスプレイをキックするためのモジュールです。
FROM node:0.12 RUN apt-get update -y RUN apt-get install -y xvfb x11-xkb-utils xfonts-100dpi xfonts-75dpi \ xfonts-scalable xfonts-cyrillic x11-apps clang libdbus-1-dev \ libgtk2.0-dev libnotify-dev libgnome-keyring-dev libgconf2-dev \ libasound2-dev libcap-dev libcups2-dev libxtst-dev libxss1 \ libnss3-dev gcc-multilib g++-multilib
上のDockerコンテナから用意したrun.shをdocker runで実行するのですがrun.shは以下の様な内容となっています。test/index.jsにUIテストが記載されているとします。現状、nightmarejsは変更が多いため、nightmarejsのlibとpackage.jsonを必要に応じてgithubから持ってきて、都度package.jsonに対してnpm install -yを行い、あまりdockerイメージの更新が発生しないようにしていますが、このプロセスはもちろんdockerイメージ作成時におこなっても良いと思います。
echo "Starting uitest" npm install -y export DISPLAY=:9.0 bash xvfb.sh start rm -rf /tmp/nightmare node_modules/.bin/mocha --harmony test/index.js if [ $? -gt 0 ]; then bash xvfb.sh stop exit 1 fi bash xvfb.sh stop
このxvfb.shがElectron用の仮想ディスプレイをコントロールしており、このスクリプトは以下のものを利用しています。
ここまでくればあとは簡単でテストをtest/index.jsに記載するだけなのですが、その書き方はnightmarejsのテストコードが参考になると思います。
一つ簡単なテストを書いてみましょう。ログインして、ある画面まで遷移してその画面内の#itemに特定のHTML要素があるかをmochaとchai.expectを利用してテストする、おそらく一番シンプルなケースです。
// Nightmareは一つ下のディレクトリのlibを参照 var Nightmare = require('..'); var expect = require('chai').expect; require('mocha-generators').install(); var url = 'ここにサーバのURL(alpacaではDockerコンテナにアクセスするのでlocalhostを指定します。)'; describe('UI Test', function () { it('Nightmare should be constructable', function*() { var nightmare = Nightmare(); expect(nightmare).to.be.ok; yield nightmare.end(); }); describe('Walkthrough', function () { var nightmare; beforeEach(function() { nightmare = Nightmare(); }); afterEach(function*() { yield nightmare.end(); }); it('チェックしたい文字列が#itemにあることのテスト', function*() { var item = yield nightmare .goto(url + "account/signin") .type("input[name=email]", "ここのテスト向けemailアドレス") .type("input[name=password]", "ここにパスワード") .click('input[type=submit]') .wait("#item") .evaluate(function(){ return $("#item").html(); }); expect(item).to.have.string('チェックしたい文字列'); }); }); });
簡単ですね。やはりevaluate内でなんでもできるのがよいです。ポイントはwait関数にmsec指定は利用せず、セレクターを使って待つようにする点です。これをnightmarejsのサンプルのようにwait(500)などで待つと、circleci上で性能がばらついた時にテストがフェイルして悲しい思いをします。NighgmareJSにはscreenshotやpdfを取得する機能もあるので、UIの状態を定期的にcircleciからSlackにポストするなどの用途にも使えそうです。
このような感じでNightmareJS+DockerによりUIテストも簡単にCircleCIなどの様々な環境に導入できますので、興味ありましたら、ぜひトライしてみてください。最近はDockerコンテナをそのまま実行できるクラウドサービスも多いので、UI監視インスタンスを立てて、なにか問題があったらSlackに問題起きた画面を通知みたいなソリューションも簡単にできそうです。
ディープラーニングを使ったトレーディングプラットフォームのCapitalicoももちろんこのノウハウを使って開発されています。現在、期間限定でクローズドベータを実施しており、みなさんのご意見をお待ちしています。すぐにでもベータテストに申し込んで、Capitalicoが実現する新しいトレーディング時代をお楽しみください!