javascript、いや、jQueryのテストツールQUnit使ってみた
- re_shikajiro
- 2011年2月9日
Table of Contents
しかだよ。
久々にajaxなお仕事に携わってjavascript(むしろjQuery)書きました。んでテストコード書きたいので調べたらJSUnitとQUnitを見つけました。QUnitはjQueryから派生したツールらしいので迷わずQUnitにしました。jQueryのテストが簡単に書けるのでいいですね。
functionのテスト
テストはこんな感じ。たぶん動くよ。
//テスト対象のサンプルコード /* * px文字列を数値にする。 * sample "100px" -> 100 */ var changePxInt = function(px_str){ if('number' == typeof(px_str)){ return px_str; } var index = px_str.search("px"); if(index < 0){ return 0; } return Number(px_str.slice(0,index)); };
//テストコード $(function(){ module("function test");test(<span class="synConstant">"changePxInt function"</span>, <span class="synIdentifier">function</span>()<span class="synIdentifier">{</span> <span class="synIdentifier">var</span> test1 = changePxInt(<span class="synConstant">"20px"</span>); equal(20, test1, <span class="synConstant">"normal test"</span>); <span class="synIdentifier">var</span> test2 = getPxInt(<span class="synConstant">"0px"</span>); equal(0, test2, <span class="synConstant">"normal test not number"</span>); <span class="synIdentifier">var</span> test3 = getPxInt(<span class="synConstant">"px"</span>); equal(0, test3, <span class="synConstant">"abnormal test bad string px"</span>); <span class="synIdentifier">var</span> test4 = getPxInt(<span class="synConstant">"20p"</span>); equal(0, test4, <span class="synConstant">"abnormal test bad string 20p"</span>); <span class="synIdentifier">var</span> test5 = getPxInt(<span class="synConstant">""</span>); equal(0, test5, <span class="synConstant">"abnormal test empty"</span>); <span class="synIdentifier">}</span>);
});
イベントのテスト
clickとかhoverとかのイベントのテストはこんな感じ。bindとtriggerでイベントを意図的に呼んでテストする感じ。
//テスト対象のサンプルコード $(function(){ /* * 画像へのマウスオーバーで画像を変換する。 */ $.fn.imgOver = function(param){ $(this).hover(function(){ var src = $(this).attr("src"); var prefix = src.slice(src.lastIndexOf('.')); var name = src.slice(0,src.lastIndexOf('.')); var onname = name + param + prefix; $(this).attr("src",onname); },function(){ var src = $(this).attr("src"); var outname = src.replace(param,""); $(this).attr("src",outname); }); return this; }; });
//テストコード $(function(){ test("hover", function(){<span class="synIdentifier">var</span> $img = $(<span class="synConstant">"<img>"</span>).attr(<span class="synConstant">"src"</span>, <span class="synConstant">"test.jpg"</span>).imgOver(<span class="synConstant">"_o"</span>); <span class="synComment">//testを評価するfunction</span> <span class="synIdentifier">var</span> over_handler = <span class="synIdentifier">function</span>(<span class="synStatement">event</span>, data)<span class="synIdentifier">{</span> equal(<span class="synConstant">"test_o.jpg"</span> , img.attr(<span class="synConstant">"src"</span>), <span class="synConstant">"normal test mouse over"</span>); <span class="synIdentifier">}</span>; <span class="synIdentifier">var</span> out_handler = <span class="synIdentifier">function</span>(<span class="synStatement">event</span>, data)<span class="synIdentifier">{</span> equal(<span class="synConstant">"test.jpg"</span> , img.attr(<span class="synConstant">"src"</span>), <span class="synConstant">"normal test mouse out"</span>); <span class="synIdentifier">}</span>; <span class="synComment">//bindで評価するfunctionを渡して、</span> <span class="synComment">//triggerでイベントを呼ぶ</span> $img.bind(<span class="synConstant">"mouseover"</span>, over_handler) .trigger(<span class="synConstant">"mouseover"</span>) .unbind(<span class="synConstant">"mouseover"</span>, over_handler); $img.bind(<span class="synConstant">"mouseout"</span>, out_handler) .trigger(<span class="synConstant">"mouseout"</span>) .unbind(<span class="synConstant">"mouseout"</span>, out_handler); <span class="synIdentifier">}</span>);
});
非同期処理のテスト
例えばjsonを取得してそのデータを元にごにょごにょする場合は、テストの実行と、データを取得するまでのズレが発生するのでsetTimeoutでいい感じに調整する。
//testじゃなくてasyncTestを使う。 asyncTest("find", function(){<span class="synComment">//テストしたい非同期な処理</span> $.getJSON(<span class="synConstant">"index.<a class="keyword" href="http://d.hatena.ne.jp/keyword/json">json</a>"</span>, <span class="synIdentifier">function</span>(data)<span class="synIdentifier">{</span> <span class="synIdentifier">this</span>.model = <span class="synStatement">new</span> modelObj(data); <span class="synIdentifier">}</span>); <span class="synComment">//第3引数に数値を渡し、実行するタイミングを遅らせる。とりあえず100mにしてみた。</span> setTimeout(<span class="synIdentifier">function</span>()<span class="synIdentifier">{</span> <span class="synComment">//100ms経ったら動き出す!</span> start(); <span class="synIdentifier">var</span> test1 = <span class="synIdentifier">this</span>.model.find(<span class="synConstant">"test"</span>); equal(<span class="synConstant">"test"</span>, test1.name, <span class="synConstant">"normal test"</span>); <span class="synIdentifier">var</span> test2 = <span class="synIdentifier">this</span>.model.find(<span class="synConstant">"shikajiro"</span>); equal(<span class="synStatement">undefined</span>, test2, <span class="synConstant">"abnormal test"</span>); <span class="synIdentifier">}</span>,100); <span class="synIdentifier">}</span>);
前処理、後処理
テスト毎に前処理後処理はmoduleの第2引数にfunctionを定義できる。setupに前準備の処理、teardownにテスト後の処理(オブジェクトの後始末とか)を書く。
module("async test",{ setup: function() { $.getJSON("index.json", function(data){ this.model = new modelObj(data); }); }, teardown: function(){ this.model = undefined; } });
感想
今回はテストファーストじゃなくて実装した後にテストコード書いたんだけど、テストコードを書けば書くほど設計の悪さが浮き彫りになりました。やっぱテストは大事ですね。リファクタリングも出来るようにもなったし大満足。javascriptの仕様をあんまり理解してないので、うまく書けない。(´・ω・`)
とりあえず上記くらいのパターンが書ければだいたいのテストはできると思う。アニメーションのテストは難しそう。今回は書かないことにした。今度本気出す。( ー`дー´)キリッ
まとめサイト
わかりやすい使い方、詳細は以下サイトを見たほうがいいです。
QUnit - jQuery JavaScript Library