Slim3でClosure LibraryのUnitTestを使ってみた・・・が。
UnitTestが無いコードは、レガシーコード!と言われるぐらい昨今UnitTestは重要なものとなっています。
僕もフレームワークに求めるのはテストのしやすさなのかも!と思ってます。
ClosureLibraryにもUnitTestをするために機能があります。
JsUnitを使ったテストをサポートしており、ブラウザ上で実行できるようになっています。
こちらのBlog記事に詳しく書いて下さってます。
Closure Library 超入門 〜テスト編〜 - present
さて、それをSlim3で使う場合、どうするかですが、以下のようにしてみました。
war
| all_test.html //全テスト実行用のhtml
+--closurelibrary
|
+--ajaxsample
+--files
| scripts.js
| stylesheet.css
+--gss
| stylesheet.gss
+--scripts
| app.js
| deps.js
| ajaxsample_test.html //これがUnitTestのhtml
| index.html
| depswriter.bat
ただ、Production環境にはUnitTest用のhtmlをUploadしたくないので、
appengine-web.xmlのstatic-filesとresource-filesに
これで、Production環境ではUnitTest用のhtmlが、404になるようになりました。
それでは、UnitTest用のhtmlの中身です。
all_test.html
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>AllTest</title> <link rel="stylesheet" href="/closure-library/closure/goog/css/multitestrunner.css"> </head> <body> <!--ここにテストランナーを表示--> <div id="runner"> </div> <script type="text/javascript" src="/closure-library/closure/goog/base.js"> </script> <script type="text/javascript"> goog.require("goog.testing.MultiTestRunner"); goog.require("goog.dom"); </script> <script type="text/javascript"> var testRunner = new goog.testing.MultiTestRunner(); var testPaths = ["/closurelibrary/ajaxsample/ajaxsample_test.html"]; testRunner.addTests(testPaths); testRunner.render(goog.dom.getElement("runner")); </script> </body> </html>
サンプルから持ってきたもの、そのままです。
一番、上に張ったBlog記事から持ってきたものなので、そちらをご覧ください。
動くものが見たい方もいるかと思うので、Production環境でも動くようにしてみました。
中身は薄いですが・・・。
ログイン - Google アカウント
ajaxsample.html
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>Ajax Sample Test</title> <script src="/closure-library/closure/goog/base.js"> </script> <script> goog.require('goog.testing.AsyncTestCase'); goog.require('goog.testing.jsunit'); </script> <script src="deps.js"> </script> </head> <body> <div id="xhr-indicator"> アクセス中 </div> <script> goog.require('ajaxsample.App'); </script> <script> var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall(); function testXhrIo(){ ajaxsample.App.getInstance().sendHoge(); } </script> </body> </html>
そして、こちらが問題でして・・・。
タイトルの"・・・が"の部分です。
どう書けば、自分がやりたいUnitTestができるのか分からなかったのですね・・・。
とりあえず、ajaxsample.Appのメソッドを呼ぶことはできました。
しかし、今回テストしようとしたのは、Ajax部分です。
できれば、Serverとのやり取りの部分をMock化して、やりたかったのですが、
どうすれば良いのか分かりませんでした。
Google API Expertが解説する Closure Libraryプログラミングガイド
- 作者: 伊藤千光
- 出版社/メーカー: インプレス
- 発売日: 2010/12/10
- メディア: 単行本(ソフトカバー)
- 購入: 4人 クリック: 183回
- この商品を含むブログ (15件) を見る
僕の応用力が足りないため、なかなか思いつかず・・・。
テストしようとしているAjaxのメソッドはtinywordと同じくXhrManagerを利用しています。
本にはXhrManagerのsend()メソッドをMockにするという手もあると書いてあるのですが、
どうやって?というレベルで止まってます・・・。
また、何故か置いてある以下の部分。
<div id="xhr-indicator"> アクセス中 </div>
Ajax通信時にインジケーターを表示しているのですが、中でこのdivを取得しています。
そのため、これが無いとErrorになるので、置いているのですが、これもどうすれば良いのか・・・。
まだまだ、ClosureLibraryのUnitTestを扱うためには修行が必要そうです。
こちらもProduction環境で動くようにしてみました。
ログイン - Google アカウント
上にアクセス中とか出ているのが、インジケーターです。
妙にそれっぽく出ていますが、出そうとして出しているわけではなく、上記の理由により出ています。
以下がテストしようとしているソースです。
index.html
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>Ajax Sample</title> <link rel="stylesheet" href="/tinyword/files/stylesheet.css"> <script src="/closure-library/closure/goog/base.js"> </script> <script src="deps.js"> </script> <!-- <script src="files/scripts.js"> </script> --> </head> <body> <h1 id="title">AjaxSample</h1> <div id="xhr-indicator"> アクセス中 </div> <!-- このdiv内にアプリケーションUIを表示する --> <div id="main"> </div> <script> goog.require('ajaxsample.App'); </script> </body> </html>
app.js
goog.provide('ajaxsample.App'); goog.require('goog.events.EventHandler'); goog.require('goog.ds.DataManager'); goog.require('goog.ds.JsDataSource'); goog.require('goog.net.XhrIo'); goog.require('goog.net.XhrManager'); goog.require('goog.object'); goog.require('goog.fx.dom'); goog.require('goog.History'); goog.require('goog.Uri'); goog.scope(function(){ var dom = goog.dom; var events = goog.events; var EventType = goog.events.EventType; var DataManager = goog.ds.DataManager; var JsDataSource = goog.ds.JsDataSource; /** @constructor */ ajaxsample.App = function(){ this.initialize_(); }; goog.addSingletonGetter(ajaxsample.App); // アプリケーションを初期化 ajaxsample.App.prototype.initialize_ = function(){ // イベントハンドラを管理するためのEventHandlerを生成 this.eventHandler_ = new events.EventHandler(this); this.xhrManager_ = new goog.net.XhrManager(); this.nextXhrId_ = 1; //インジケータ設定 var indicator = dom.getElement('xhr-indicator'); this.fadeInIndicator_ = new goog.fx.dom.FadeInAndShow(indicator, 10); this.fadeOutIndicator_ = new goog.fx.dom.FadeOutAndHide(indicator, 1000); this.fadeOutIndicator_.play(true); this.eventHandler_.listen(this.xhrManager_, goog.net.EventType.READY, this.onXhrReady_); this.eventHandler_.listen(this.xhrManager_, goog.net.EventType.COMPLETE, this.onXhrComplete_); }; ajaxsample.App.prototype.sendHoge = function() { this.sendRequest('get', { 'text': 'hoge' }, goog.bind(this.hogeComplete, this), 'PUT', { '#text': name }); }; ajaxsample.App.prototype.hogeComplete = function() { console.log('hogeComplete!!!'); }; ajaxsample.App.prototype.sendRequest = function(path, query, callback, method, opt_content){ var url = goog.Uri.parse('/closurelibrary/ajaxsample/' + path); goog.object.forEach(query || {}, function(value, key){ url.setParameterValue(key, value); }, this); var headers = {}, body = null; if (opt_content) { body = goog.json.serialize(opt_content); headers['Content-Type'] = 'application/json'; } return this.xhrManager_.send(this.nextXhrId_++, url, method, body, headers, 0, goog.bind(this.processRequest_, this, callback)); }; ajaxsample.App.prototype.processRequest_ = function(callback, e){ var xhr = e.target; if (xhr.isSuccess()) { callback && callback(xhr.getResponseJson('while(1);')); } else { alert(xhr.getResponseText()); } }; ajaxsample.App.prototype.onXhrReady_ = function(e){ if (this.xhrManager_.getOutstandingCount() == 1) { this.fadeOutIndicator_.stop(false); this.fadeInIndicator_.play(true); } }; ajaxsample.App.prototype.onXhrComplete_ = function(e){ if (this.xhrManager_.getOutstandingCount() == 1) { this.fadeInIndicator_.stop(false); this.fadeOutIndicator_.play(true); } }; ajaxsample.App.getInstance(); });
最後にGoogleCodeのリポジトリのリンクを貼っておきます。
Google Code Archive - Long-term storage for Google Code Project Hosting.
上記以外の物も色々と置いてますが、参考程度に・・・。