2014年1月17日、ベルサール新宿グランドにて開催された「エンジニアサポートCROSS 2014」の中の1セッション「実況解説つき!ペアプロでわかるJavaScriptテスト入門」をレポートします。
エンジニアサポートCROSS 2014とは
「複数の技術を身につけなければWebサービスは作れない=クロスしないと生きていけない」をテーマに、「エンジニアサポート新年会2012 CROSS」として第一回が2012年に開催された勉強会イベント、それがCROSSです。今年で3回目になります。
「実況解説つき!ペアプロでわかるJavaScriptテスト入門」
このセッションは「JavaScriptで書かれたよくあるコードをベースに、ペアプロでテストコードを足していく様子を解説者が説明する」という内容になります。
それでは、さっそくセッションの模様を見ていきましょう。
登壇者
本セッションの登壇者は、セッションオーナーの吾郷協さん(@kyo_ago)、本セッションで実際にコードを書く初心者役として参加の古屋徹さん、書かれたコードの解説者として参加の佐藤鉄平さん(@teppeis)と和田卓人さん(@t_wada)です。
はじめに
本セッションで使われたソース一式は吾郷さんがGitHub上にアップロードしています。
ダウンロードしたファイルを確認してみると、testem.jsonには今回使用するtestemというフレームワークの設定情報が記述されています。
setup.jsはテストが開始されるときに一番最初に一度だけ呼ばれるものです。ここではテストコードをわかりやすくするために単純にtrueを返すだけのupdateLabel()という関数が記述されており、これが読み出されるかをテストしていきます。mocha.setup()
はMochaというフレームワークも利用するので、そのための記述となります。
また、/js/ディレクトリ内にテストしたいjsファイルが、/test/ディレクトリ内にテストするためのjsファイルが含められています。
準備
まずはコマンドラインからtestemのインストールを行います。
インストールができたらtestem
コマンドを打ってtestemを起動させましょう。testemはJavaScriptを実際にウェブブラウザ上で実行してOK/NGを返してくれます。http://localhost:7357/にサーバが立ち上がるのでウェブブラウザでアクセスすると下記のような画面が表示されます。
これは既に用意されていた/test/0.jsの結果が返ってきている状態となります。
テストツールについて
ここで、マイクをバトンタッチして佐藤さんからJavaScriptのテストツールについての解説がありました。
佐藤さん曰く「昔はJavaScriptでテストは書きにくかったですが、最近は様々なツールが発達してきて書きやすくなってきた」とのことです(上図参考)。
今回使っているのはこの図でいう3つ。具体的には次のものです(実はモックライブラリのSinon.JSも使っていますがそれは後述)。
- リモートテストランナーとしてのtestem
- テスティングフレームワークとしてのMocha
- describe/itというようなテスト形式で書いてそれを実行するというところをやってくれる
- アサーションライブラリとしてのexpect.js
expect(window.r).to.be(undefined);
のような形で、window.rはundefinedですよというのをアサーションする(確認する)
また、和田さんからは、他の言語だと例えばJavaだとJUnitなど支配的なテスティングフレームワークがありますが、JavaScriptだと流行りのものが移ろいやすく、現在はJasmineとMochaの二強状態。Jasmineは全部入りで、Mochaは自分の好きなものを組み合わせて使える、という違いがあるという話がありました。
なぜtestemが必要かということについては、JavaScriptのテストの難しいところは必ずブラウザから確認する必要があり、testemを使うと実際のブラウザに対してテストでき、IEでもFirefoxでもChromeでも、もちろんモバイルのブラウザからも確認が可能となるということです。
例題1
それでは本題。よくある、ボタンイベントを取得するテストです。
これに対するテストコードはこちら。
ここで、本来であれば元のコードに全く手を加えずにテストできるのが一番良いですが、jQueryを使っている場合はそれが難しいので少し手を加えるそうです。
setup.jsに次の一文を追加します。
また、/js/1.jsの一行目を次のように書き換えます。
これによって、testInitが定義されている場合はtestInitを使って初期化をし、そうでなければ$
を使って初期化をするようになります。本来であれば読み込まれてすぐに$
関数で初期化されてしまうのを別のもので初期化するようにしています。
この状態でブラウザを見てみると、2つ目のテストの結果(1.jsのテストの結果)が返ってきているのがわかります。
ここで使ったSinon.JSというライブラリは、このような既存のメソッドを置き換えるモックライブラリやテストダブルライブラリと言われているものであり、JavaScriptのテストではデファクトになっていると佐藤さんから解説がありました。
和田さんからもモックライブラリについての詳しい解説があり、簡単に言うとテストしにくい仕組みをテストしやすい偽物で置き換えてテスト可能な形にしていくためのものと言及していました。
例題2
次はこれまたよくあるタイマーを用いたコードを見ていきます。
10msだけなら待つことは簡単ですが、例えば3分待つといったコードがある際に実際にその時間待たなくてもテストできる仕組みがあるそうで、ここではその解説がメインとなりました。
このように、Sinon.JSを用いることでコード内の時間も自由に扱うことが可能となるそうです。sinon.useFakeTimers()
によりJavaScript世界での時間が止まり(時間が勝手には進まなくなる、setTimeout()
、setInterval()
、date()
などが止まる)、また、.tick()
で時間を自由に進められるようになるとの解説が佐藤さんからありました。
また、JavaScript自体が非同期な言語であるためにこういったテストはよくあるという話の中、上記のような例のほかにMochaを使った次のような例も解説しました。
非同期のテストをする際には基本的にはsinon.useFakeTimers()
をオススメしますが、コールバック形式のAPIをとる関数を使うときのテストなどでこのdone
がかなり使いやすいということです。
例題3
その次に、テストが大変なajaxを用いたコードです。
これのテストをそのままやろうとすると、サーバから値が返ってこないとダメで、そうするとJavaScriptだけでテストが完結できません。また、サーバにファイルを上げたり、サーバがレスポンスをきちんと返してこないとテストできません。そこで、テストできる形に置き換えて、テストできるようにします。
コード中、sinon.fakeServer.create();
によってhttpリクエストが内部的に書き換えられて、実際には通信を行わずこちらから指示をしない限りは何もしない状態になっています。また、このコードのように事前にrespondWith
の内容を設定する方法のほかに、respond
の中で書くこともできるそうです。
例題4
最後に、これまでの例題の総復習のような例題が出ました。
この時点で残念ながら時間切れとなってしまい、これの回答はセッション中は行われませんでしたが、前述の吾郷さんのGitHubにこれまでの回答も含め収められてますのでぜひみなさんも確認してみてください(吾郷さん親切!)。
テストケースを日本語を書くことについて
ここで、佐藤さんから「テストケースを日本語で書くことについて」の是非の話がありました(上記で見てきたように、本セッションではit
の中で日本語でテストケースを書いていますね)。
これに対する和田さんの回答は「実行上問題ないのであれば、かつ日本人中心のチームであれば積極的に日本語使っていきましょう」とのことでした。佐藤さんからも「英語だからってテストを書くハードル高くなるのはつまらないよね」という実際的な話もありました。
まとめ
今回のセッションではJavaScriptでほとんどテストを書いたことが無い人が今日からでもユニットテストが始められるように、testem/Mochaなどのフレームワークを用いたテストのやりかたについて実際のコードに触れながら一からの解説が行われました。
実は、冒頭で佐藤さんから会場に対して「JavaScriptでテストを書いたことがある人はどのくらいいますか?」という質問があり、書いたことがある人は会場の5割ほど、また、ユニットテストを当たり前にやっている人は1割ほどという結果でした。
記事の中で見てきたようにテストのための使いやすそうなツールがこれだけある中、本セッションによって(またはこの記事によって)JavaScriptでテストを書く人が増え、より良いコードがこの世に増えていく助けになれば幸いです。
(私もきちんとテストがんばろう…と思います。)