Grunt + QUnit + PhantomJS でテストを自動実行してくれる環境を構築する

公開日: : 最終更新日:2014/05/05 JavaScript , , , , ,

先日投稿した GoogleMap サンプルのプロジェクトに Grunt の LiveReload を有効にする設定を追加しました の記事で Grunt の LiveReload をプロジェクトに適用する方法を紹介しました。
HTML や JavaScript などを保存すると自動でブラウザがリロードされるため、エディタからブラウザに切り替える手間が省けて非常に快適です。

快適過ぎる環境に欲が出てきて、ユニットテストも自動実行してくれるような環境構築を検証してみました。

いつものようにプロジェクトは GitHub に登録してあります。

このプロジェクトを clone してもらえばすぐに自動実行を体験できるので、ぜひお試しください。

icatch-automatic_2714993937_mini

photo credit: Swami Stream via photopin cc

目次

1. 背景

テスティングフレームワークは QUnit を採用

JavaScript のテスト系ライブラリは以前に比べると充実しているようで、ググるといろいろヒットしますね。
ヒットするのはいいんですが、DOM をちょろっといじるとか Ajax で JavaScript を使ってます程度の人間には正直よくわからないところがあって。。。

なので、使い慣れた JUnit と同系のテスティングフレームワーク QUnit を採用。
BDD をやりたいわけでもないし、シンプルにユニットテストが書ければいいので。

grunt-contrib-qunit を使ってテストを実行

テストの実行は、いちいちコマンドを実行するのではなく Grunt の LiveReload を使って JavaScript を保存したタイミングで走るように自動化できればいいなぁと思い、ググると Grunt と QUnit を結ぶプラグイン gruntjs/grunt-contrib-qunit が見つかりました。

「もしかしてホントにできるんじゃないの?」ってことで、環境構築を開始しました。

2. この記事の前提

いろいろ試行錯誤した結果、テストケースとなる JavaScript を保存したタイミングでテストが自動実行されるようになりました。

その手順をこれから説明していきますが、node.js や Grunt 本体のインストール、それから Grunt の gruntjs/grunt-contrib-connect プラグインと gruntjs/grunt-contrib-watch プラグインの設定などは省略します。

次の記事でそれらは扱っていますので、ぜひ参考にしてみてください。

以下の内容は、この環境が前提として書いていますのでよろしくお願いします。

3. grunt-contrib-qunit を使ってユニットテストを実行してみる

3-1. grunt-contrib-qunit のインストール

プロジェクト直下に移動し、Node.js のインストールと同時にインストールされた “Node.js command prompt” から次を実行します。
コマンドの実行が成功すると、このプロジェクトで grunt-contrib-qunit が使えるようになります。

$ npm install grunt-contrib-qunit --save-dev
$ ls -1 node_modules 
grunt
grunt-contrib-connect
grunt-contrib-qunit  ## ← これが追加されたプラグイン
grunt-contrib-watch

grunt-contrib-qunit プラグインを使ったユニットテストの実行は、ブラウザではなく PhantomJS で行われるため、プラグインをインストールすると PhantomJS もインストールされます。
以下は、上記 npm コマンドを実行した時に端末に出力されたログの抜粋です。

Downloading http://cdn.bitbucket.org/ariya/phantomjs/downloads/phantomjs-1.9.7-windows.zip
Saving to C:\Users\xxx\AppData\Local\Temp\phantomjs\phantomjs-1.9.7-windows.zip
Receiving...
Received 782K...
Received 1563K...
Received 2344K...
Received 3126K...
Received 3907K...
Received 4688K...
Received 5470K...
Received 6251K...
Received 6823K total.
Extracting zip contents
Copying extracted folder C:\Users\xxx\AppData\Local\Temp\phantomjs\phantomjs-1.9.7-windows.zip-extract-1395409853934\phantomjs-1.9.7-windows -> C:\Users\xxx\work\workspace\googlemap-sample\node_modules\grunt-contrib-qunit\node_modules\grunt-lib-phantomjs\node_modules\phantomjs\lib\phantom Writing location.js file
Done. Phantomjs binary available at C:\Users\xxx\work\workspace\googlemap-sample\node_modules\grunt-contrib-qunit\node_modules\grunt-lib-phantomjs\node_modules\phantomjs\lib\phantom\phantomjs.exe
grunt-contrib-qunit@0.4.0 node_modules\grunt-contrib-qunit
└── grunt-lib-phantomjs@0.5.0 (eventemitter2@0.4.13, semver@1.0.14, temporary
@0.0.8, phantomjs@1.9.7-1)

次に、Gruntfile.js に次の1行を追加します。

grunt.loadNpmTasks('grunt-contrib-qunit');

gruntjs/grunt-contrib-qunit の “Getting Started” や “Qunit task” を見ると、これだけで動くような感じに書いてあったので、さっそく grunt qunit コマンドを実行してみると、、、

$ grunt qunit
>> No "qunit" targets found.
Warning: Task "qunit" failed. Use --force to continue.

Aborted due to warnings.

Task が見つからないと怒られてしまいました。
なので、Gruntfile.js に次の1行を追加して再度実行。

// `qunit` コマンドで実行するタスクを定義する
grunt.registerTask('qunit', ['qunit']);
$ grunt qunit

:
:

(node) warning: Recursive process.nextTick detected. This will break in the next
 version of node. Please use setImmediate for recursive deferral.

util.js:35
  var str = String(f).replace(formatRegExp, function(x) {
                      ^
RangeError: Maximum call stack size exceeded

まだ動きません。

3-2. QUnit の設定を追加したら動くようになった

更に gruntjs/grunt-contrib-qunit を読んでいくと qunit コマンドのオプションが載っていました。
どこにある HTML ファイルを対象にするか?という設定と思われるものです。

このマニュアルと同じく test ディレクトリに HTML と JavaScript ファイルを置いていたので Gruntfile.js に次を追記。

grunt.initConfig({
    :
    :
    qunit: {
        all: ['test/**/*.html']
    }
});

また、次の1行は削除しました。

// `qunit` コマンドで実行するタスクを定義する
grunt.registerTask('qunit', ['qunit']);

で、再度 grunt qunit コマンドを実行してみると、今度は動きました。

$ grunt qunit
Running "qunit:all" (qunit) task
Testing test/stock-test.html ...OK
>> 8 assertions passed (162ms)

Done, without errors.

これで grunt-contrib-qunit を使って、コマンドラインからユニットテストを実行できるようになりました!

4. テストケースとなる JavaScript を保存したタイミングでテストを自動実行する

4-1. ファイルを保存したタイミングでテストを自動実行する

grunt-contrib-qunit を使ってユニットテストが走るようにはなりましたが、エディタからコンソールに切り替えるのは面倒。
ということで、更に gruntjs/grunt-contrib-qunit を読んでいくと “Using the grunt-contrib-connect plugin” という項目に、それらしい設定が載っていました。

これをベースに Gruntfile.js を次のように変更してみると、grunt qunit コマンドを実行しておけばファイル変更をトリガにユニットテストが実行されるようになりました!

module.exports = function(grunt) {
    grunt.initConfig({
        // パッケージファイルを読み込む
        pkg: grunt.file.readJSON('package.json'),

        // grunt-contrib-connect の設定
        connect: {
            site: {
                options: {
                    // ドメイン名は 'localhost' とする
                    hostname: 'localhost',
                    // 接続するポートは 9000 とする
                    port: 9000
                }
            },
            server: { // ← ここを追加
                options: {
                    port: 9000,
                    base: '.'
                }
            }
        },

        // grunt-contrib-watch の設定
        watch: {
            // プロジェクト下のある 全 HTML, CSS, JavaScript を監視対象とする
            files: ['**/*.html', '**/*.css', '**/*.js'],
            options: {
                // 上記のファイルが更新されたらブラウザをリロードする
                livereload: true
            },
            tasks: ['qunit'] // ← ここを追加
        },

        qunit: {
            all: ['test/**/*.html']
        }
    });

    // LiveReload するために必要なタスクをロードする
    grunt.loadNpmTasks('grunt-contrib-connect');
    grunt.loadNpmTasks('grunt-contrib-watch');
    // grunt-contrib-qunit を有効にするためのタスクをロードする
    grunt.loadNpmTasks('grunt-contrib-qunit');

    // `grunt` コマンドで実行するデフォルトタスクを定義する
    grunt.registerTask('default', ['connect', 'watch']);

    // `grunt test` で実行するタスクを定義する
    grunt.registerTask('test', ['connect', 'watch', 'qunit']);
};

これでユニットテストの自動実行ができるようになったとはいえ、すべての HTML, JavaScript, CSS の変更でテストが走ってしまうので無茶があります。
今度はこれを整理します。

4-2. テストケースとなる JavaScript を保存したタイミングでテストを自動実行する

無茶があった設定を次のように整理しました。

module.exports = function(grunt) {
    grunt.initConfig({
        // パッケージファイルを読み込む
        pkg: grunt.file.readJSON('package.json'),

        // grunt-contrib-connect の設定
        connect: {
            live: { // LiveReload 用の設定
                options: {
                    hostname: 'localhost', port: 9000
                }
            },
            qunit: { // QUnit 用の設定
                options: {
                    port: 9090, base: '.'
                }
            }
        },

        // grunt-contrib-watch の設定
        watch: {
            live: { // LiveReload 用の設定
                // プロジェクト下のある 全 HTML, CSS, JavaScript を監視対象とする
                files: ['**/*.html', '**/*.css', '**/*.js'],
                options: {
                    livereload: true // 上記のファイルが更新されたらブラウザをリロードする
                }
            },
            qunit: { // QUnit 用の設定
                files: ['test/**/*.js'], // test ディレクトリにある全 JavaScript を監視対象とする
                tasks: ['qunit']
            }
        },

        // grunt-contrib-qunit の設定
        qunit: {
            all: ['test/**/*.html'] // test ディレクトリにある HTML を実行対象とする
        }
    });

    // LiveReload するために必要なタスクをロードする
    grunt.loadNpmTasks('grunt-contrib-connect');
    grunt.loadNpmTasks('grunt-contrib-watch');
    // grunt-contrib-qunit を有効にするためのタスクをロードする
    grunt.loadNpmTasks('grunt-contrib-qunit');

    // `grunt` コマンドで実行するデフォルトタスクを定義する
    grunt.registerTask('default', ['connect:live', 'watch:live']);

    // `grunt test` で実行するタスクを定義する
    grunt.registerTask('test', ['connect:qunit', 'watch:qunit', 'qunit']);
};

LiveReload 用の設定は “live” という名前を付け、QUnit 用の設定には “qunit” という名前にしました。
これに伴い、”grunt.registerTask” を次のように書き換えました。

  • grunt.registerTask(‘default’, ['connect:live', 'watch:live']);
  • grunt.registerTask(‘test’, ['connect:qunit', 'watch:qunit', 'qunit']);

また、”files: ['test/**/*.js']” とすることで、テストケースとなる JavaScript を保存したタイミングでテストが自動実行されるようにファイルの監視範囲を絞りました。

あとは、ユニットテストを書くときに grunt qunit を実行しておけば、勝手にテストを実行してくれます。
エディタからフォーカスを移す必要がないのでめちゃくちゃ快適です!
これはすごい!

5. まとめ

以上、勝手にユニットテストを実行してくれる Grunt の設定でした。

この設定ホントにオススメなんで、ぜひとも試していただきたいです。

6. プログラムを Github に登録しました

今回の記事で使用したサンプルプログラムは Github に登録してあります。

7. その他の JavaScript に関する記事

その他の JavaScript に関する記事は次の通りです。
気になる記事があったらぜひチェックしてみてください!

Googleアドセンス用(PC)

  • このエントリーをはてなブックマークに追加
  • follow us in feedly

関連記事

icatch-disappear_7186028511_mini-thumbnail

GoogleMapsAPIを使ってMarkerの表示と非表示を切り替える方法

GoogleMaps API(v3) を使って地図上に表示されている Marker を非表示にする方

記事を読む

icatch-class_303144538_mini-thumbnail

JavaScript のクラス定義

最近 JavaScript を使う機会が増えてきて、快適にコードが書けるように環境を少しずつ整えてき

記事を読む

icatch-drill_mini

Underscore.js の template を使ってドリルダウンを実装するスニペット

先日、Underscore.js の template と each を使って JSON から se

記事を読む

icatch-flag_1395019914_mini-thumbnail

GoogleMaps の JavaScript API(v3) を使って、常に1つの InfoWindow だけ表示されるように制御する

前回アップした GoogleMaps の JavaScript API (v3) を使って地図上に複

記事を読む

icatch-googlemap_3384984991_mini-thumbnail

住所から GoogleMap を表示するサンプル

ここ直近の2プロジェクトで緯度・経度(もしくは住所)から GoogleMap を表示する要件がありま

記事を読む

icatch-switch_mini

jQuery を使って表示する画像を setInterval を使って切り替える

画面に表示する画像を一定期間で切り替えるスニペットです。 スライドショー的なプラグインで済ますこと

記事を読む

icatch-mist_mini

jQuery を使ってページの opacity を徐々に上げて fadeIn して見せる

仕事で HTML を使ってコンテンツをいくつか作成しました。 その際に他でも使い回しが効くスニペット

記事を読む

icatch-format_mini-thumbnail

JavaScriptなどをフォーマットするGruntプラグイン grunt-jsbeautifier を使ってみる

Java を使ってプログラムを書いているときはソースコードをフォーマットするのに、JavaScrip

記事を読む

icatch-checklist_4439276478_mini-thumbnail

BootstrapValidator の基本的なバリデーションを試すサンプルを作成しました

TwitterBootstrap3 用のバリデーションチェックプラグイン BootstrapVali

記事を読む

icatch-load_6161289029_mini-thumbnail

Grunt の grunt-contrib-watch を使うと CPU 使用率が上がるので spawn: false を試してみた結果

grunt-contrib-watch を使うことで、ユニットテストを実行したり、サイトをリロードし

記事を読む

Googleアドセンス用(PC)

Message

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です


− 一 = 8

次のHTML タグと属性が使えます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Googleアドセンス用(PC)

icatch-jersey_multi_pathparams
Jerseyの@PathParamはスラッシュの間に複数指定できる

http://hoge-api/user/{id}.{format}

icatch-vagrant_box_customize
VagrantのBoxファイルをカスタマイズして独自のBoxファイルを作成する

配布されている Vagrant の Box ファイルを使って検証環境を

icatch-2015-006-1
バリデーションチェックにJava8のOptionalを使ってスマートに書く(自分比)

Web アプリのバリデーションチェックにアノテーションを使うことが増え

icatch-2015-005-1
ユニットテストの偏りを防ぐ命名規則の付け方

ユニットテスト名に以下の命名規則を付けるようにして二ヶ月ぐらい経った。

icatch-2015-004-1
Vagrantで起動したCentOS上のOctopressをホストOSから確認する設定

タイトルの通りだが、Vagrant を使って起動した CentOS に

→もっと見る

PAGE TOP ↑