実装を進める前にテストを動かしておきます。 あちこちと実装が散らかってくるとデグレードが多くなりますので、 出来るだけ手戻りがないように用意できることが望ましいと思います。
この記事の続きです。
- JavaScriptMVC - 概要 (2011年8月25日)
- JavaScriptMVC を使ってみる (2011年8月26日)
2つのテスト方法
JavaScriptMVC で利用可能なテストには次の2種類があります。
それぞれのテストはブラウザでも確認できますし、 コマンドラインからも実行できます。 コマンドラインではブラウザをシミュレートするために Envjs を使います。
モデルの取得や生成を確認するためには QUnit を使い、 ユーザーとのインタラクション (e.g. マウス操作、キーボード入力) を確認するためには FuncUnit を使います。 それぞれの方法を順番に見ていきましょう。
QUnit
テスト用の HTML ファイルを cookbook/qunit.html として用意します。
<html> <head> <link rel="stylesheet" type="text/css" href="../funcunit/qunit/qunit.css" /> </head> <body> <h1 id="qunit-header">Cookbook Application Test Suite</h1> <h2 id="qunit-banner"></h2> <div id="qunit-testrunner-toolbar"></div> <h2 id="qunit-userAgent"></h2> <ol id="qunit-tests"></ol> <div id="qunit-test-area"></div> <script type='text/javascript' src='../steal/steal.js?cookbook/qunit.js'></script> </body> </html>
テストケースを呼び出すためのスクリプトを cookbook/qunit.js として用意します。 steal.js に渡している部分のパスです。
steal .plugins('funcunit/qunit') .then('test/qunit/recipe_test') ;
test/qunit/recipe_test.js は scaffold で生成されたものです。
ブラウザーで cookbook/qunit.html を開くとテストが実行されます。 まずはすべてのテストケースが失敗しています。
テストケースをクリックすると、以下のメッセージを確認できます。
Died on test #1: Cookbook is not defined
モデルモジュールのローディングミスなので、 cookbook/qunit.js を次のように変更します。
steal .plugins('funcunit/qunit', 'jquery/model') .then('models/recipe') .then('test/qunit/recipe_test') ;
再度テストを実行すると、モデルを取得 / 生成するために GET / POST リクエストが発生します。 サーバーサイドは何も実装していませんので、すべてがエラーになります。
サーバーサイドの実装とクライアントサイドの実装は無関係に進めたいですから、 jQuery.fixture を使ってリクエストをエミュレートします。 cookbook/qunit.js で fixture プラグインも読み込むようにします。
steal .plugins('funcunit/qunit', 'jquery/model', 'jquery/dom/fixture') .then('models/recipe') .then('test/qunit/recipe_test') ;
これでテストがパスします。
コマンドラインからの実行
コマンドラインからもテストを実行できます。
$ ./funcunit/envjs cookbook/qunit.html Using Default Settings MODULE Model: Cookbook.Models.Recipe --findAll-- steal.js INFO: looking for fixture in ../cookbook/fixtures/recipes.json.get PASS PASS PASS PASS done - fail 0, pass 4 --create-- steal.js INFO: using a dynamic fixture for /recipes steal.js INFO: Model.js - publishing recipe.created PASS PASS PASS okay, expected: "dry cleaning" result: "dry cleaning" steal.js INFO: using a dynamic fixture for /recipes/1867 done - fail 0, pass 3 --update-- steal.js INFO: using a dynamic fixture for /recipes steal.js INFO: Model.js - publishing recipe.destroyed steal.js INFO: Model.js - publishing recipe.created PASS okay, expected: "chicken" result: "chicken" steal.js INFO: using a dynamic fixture for /recipes/25832 steal.js INFO: Model.js - publishing recipe.updated PASS okay, expected: "steak" result: "steak" steal.js INFO: using a dynamic fixture for /recipes/25832 done - fail 0, pass 2 --destroy-- steal.js INFO: using a dynamic fixture for /recipes/undefined steal.js INFO: Model.js - publishing recipe.destroyed steal.js INFO: Model.js - publishing recipe.destroyed PASS Destroy called done - fail 0, pass 1 ALL DONEe - fail 0, pass 10
10個のテストがすべてパスしていることが分かります。
FuncUnit
続いて、ユーザー操作に関するテストを作成します。 クリックすべき要素があるか、入力フィールドが存在するか、といったことをテストします。
テスト用の HTML ファイルを cookbook/funcunit.html として用意します。
<html> <head> <link rel="stylesheet" type="text/css" href="../funcunit/qunit/qunit.css" /> <title>FuncUnit Test</title> </head> <body> <h1 id="qunit-header">funcunit Test Suite</h1> <h2 id="qunit-banner"></h2> <div id="qunit-testrunner-toolbar"></div> <h2 id="qunit-userAgent"></h2> <ol id="qunit-tests"></ol> <script type='text/javascript' src='../steal/steal.js?cookbook/funcunit.js'></script> </body> </html>
テストケースを呼び出すためのスクリプトを cookbook/funcunit.js として用意します。 test/funcunit/recipe_controller_test.js は scaffold で生成されたものです。
steal .plugins('funcunit', 'jquery/model', 'jquery/controller') .then('controllers/recipe_controller') .then('test/funcunit/recipe_controller_test') ;
ブラウザで funcunit.html を開いてテストを実行します。 ポップアップブロックは解除しておきましょう。
コマンドラインからの実行
コマンドラインからも確認しましょう。
$ ./funcunit/envjs cookbook/funcunit.html Using Default Settings Selenium is not running. Please use steal/js -selenium to start it.
FuncUnit をコマンドラインから実行すると、裏側で Selenium を動かす必要があります。
$ pushd funcunit/java $ ln -s selenium-server-standalone-2.0b3.jar selenium-server.jar $ popd $ ./js -selenium $ ./funcunit/envjs cookbook/funcunit.html
たぶんこれで動くはずなんですが、手元の環境では Firefox が何も進みませんでした。 読み込むプラグインが足りないんだと思いますが、深追いしないことにします。
終わりに
JavaScriptMVC を使ってテストケースを動かしました。 2種類のテストがありますので、着目する部分に応じてテストを使い分けられると良さそうです。 ブラウザテストは同じ操作を再現することが煩雑だったりクロスブラウザでの確認を忘れがちですが、 Envjs を利用してコマンドラインからも実行できると、CI ツールに組み込んで使えます。
先にテストを書いておくと、とりあえず今日はここまで、という区切りを考えやすくなるかもしれません。 また、サーバーサイドの API が未実装でも、わざわざモックを用意することなく、 JSON でなんとなくデータをエミュレートできます。 最近は HTML5 を中心としたクライアントサイドのストレージも使えるようになりましたので、 HTTP をエミュレートしない場合には選択肢のひとつとして考慮すべきだと思います。
次は、モデルやコントローラーの API を HTML ドキュメントとして出力します。
0 件のコメント:
コメントを投稿