エンジニアサポートCROSS 2014 レポート

「実況解説つき!ペアプロでわかるJavaScriptテスト入門」レポート

この記事を読むのに必要な時間:およそ 5 分

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というフレームワークの設定情報が記述されています。

testem.json

{
        "src_files": [
                "js/lib/*.js",
                "test/lib/*.js",
                "setup.js",
                "js/*.js",
                "test/*.js"
        ],
        "test_page": "tests.mustache"
}}

setup.jsはテストが開始されるときに一番最初に一度だけ呼ばれるものです。ここではテストコードをわかりやすくするために単純にtrueを返すだけのupdateLabel()という関数が記述されており,これが読み出されるかをテストしていきます。mocha.setup()Mochaというフレームワークも利用するので,そのための記述となります。

setup.js

function updateLabel() {
        return true;
}
mocha.setup('bdd');

また,/js/ディレクトリ内にテストしたいjsファイルが,/test/ディレクトリ内にテストするためのjsファイルが含められています。

準備

まずはコマンドラインからtestemのインストールを行います。

npm -g install testem

インストールができたらtestemコマンドを打ってtestemを起動させましょう。testemはJavaScriptを実際にウェブブラウザ上で実行してOK/NGを返してくれます。http://localhost:7357/にサーバが立ち上がるのでウェブブラウザでアクセスすると下記のような画面が表示されます。

画像

これは既に用意されていた/test/0.jsの結果が返ってきている状態となります。

/js/0.js

function firstTest() {
        return true;
}

/test/0.js

describe('0', function() {
        it('trueが返ってくるか確認する', function() {
                expect(firstTest()).to.eql(true);
        });
});

テストツールについて

ここで,マイクをバトンタッチして佐藤さんからJavaScriptのテストツールについての解説がありました。

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

それでは本題。よくある,ボタンイベントを取得するテストです。

/js/1.js

$(function() {
        $('.jQuery.button').click(function() {
                updateLabel();
        });
});

これに対するテストコードはこちら。

/test/1.js

// 筆者注:describeでこのテストがどういうテストかを宣言
describe('1', function() {
        // 筆者注:itでこのテストがどういうルールによって展開して何を確認したいかを宣言
        it('ボタンをクリックしてupdateLabelを表示する', function() {
                // 筆者注:updateLabel()が実行されたかは外からはわからないので,
                // それがわかるように別のfunctionに置き換える。
                // Sinon.JSというライブラリを利用
                sinon.stub(window, 'updateLabel');

                // 筆者注:testInitというfunctionを実行,jQueryの$関数を一時的に置き換える(後述)
                var init = window.testInit.args.shift();
                init[0]();
                $('.jQuery.button').click();

                expect(updateLabel.called).to.eql(true);
                window.updateLabel.restore();
        });
});

ここで,本来であれば元のコードに全く手を加えずにテストできるのが一番良いですが,jQueryを使っている場合はそれが難しいので少し手を加えるそうです。

setup.jsに次の一文を追加します。

window.testInit = sinon.stub();

また,/js/1.jsの一行目を次のように書き換えます。

(window.testInit || $)(function() {

これによって,testInitが定義されている場合はtestInitを使って初期化をし,そうでなければ$を使って初期化をするようになります。本来であれば読み込まれてすぐに$関数で初期化されてしまうのを別のもので初期化するようにしています。

この状態でブラウザを見てみると,2つ目のテストの結果(1.jsのテストの結果)が返ってきているのがわかります。

画像

ここで使ったSinon.JSというライブラリは,このような既存のメソッドを置き換えるモックライブラリやテストダブルライブラリと言われているものであり,JavaScriptのテストではデファクトになっていると佐藤さんから解説がありました。

和田さんからもモックライブラリについての詳しい解説があり,簡単に言うとテストしにくい仕組みをテストしやすい偽物で置き換えてテスト可能な形にしていくためのものと言及していました。

著者プロフィール

右寺隆信(みぎてらたかのぶ)

株式会社アプリカ 開発部フロントエンジニア

Twitter:@migi
URL:http://flavors.me/migi

コメント

コメントの記入