こんにちは、YAT(@yat8823jp)です。5/19のjsdokushoという勉強会に参加してきました。
今回は「JavaScriptのテストに関する勉強会」と言うお題だったのですが、普段より人数が多かったのでグループ単位でワークショップ形式で行い、それぞれのグループごとに別々のフレームワークに関して仕様や使い方を調べると言うやり方で進めました。
1.テストとは
僕が入ったグループはQUnitと呼ばれるフレームワークを調べることになりました。QUnitはjQueryをベースにしたテストユニットです。
僕自身最近はJavaScriptを直接書くより、断然jQueryを利用して書くことのほうが多いので、このニュアンスだけでちょっと分かりやすそう!といった期待を持ってました。(ちなみにjQueryに依存しないように作られているので、JavaScriptのテストも出来ます。)
まずテストがどういうものか分からない方も居ると思うので簡単に解説すると、プログラミングで何かを作る時と言うのは、必要な機能を持った関数やパーツ(以下ユニットと呼びます。)を作って行くと思います。
(デザイナーさんだと、PhotoshopやFireworksでデザインするとき、パーツごとにグルーピングするといった感覚に近いかなと思います。)
そしてそれぞれ完成したユニットが正常に動作するかをチェックし、全体が正常に動作するかという判断を行っていくのですが
テストと言うのは、それぞれのユニット単位に対して様々な値を与え、想定した値が帰ってくるか、設計通りの動作をしているかを確認することを指します。
2.Qunitの仕様について
Qunitはqunit.jsを読み込み、実際のjsファイルとテストしたいtestのjsファイルをそれぞれ読み込んで動作せる事でテストを行う事ができます。
フレームワークのファイルはjQueryのQUnitの項目にgithubへのリンクが張ってあり、そちらからダウンロードが出来ます。
https://github.com/jquery/qunit
解凍すると様々なファイルが入っています。今回利用したのは
- qunitフォルダ
- testフォルダ
と、その中身です。
Qunit.jsで提供されているメソッドは下記の通りです。
Setup
- test( name, expected, test )
- Add a test to run.
- asyncTest( name, expected, test )
- Add an asynchronous test to run. The test must include a call to start().
- expect( amount )
- Specify how many assertions are expected to run within a test.
- module( name, lifecycle )
- Separate tests into modules.
- QUnit.init( )
- Initialize the test runner (if the runner has already run it’ll be re-initialized, effectively resetting it). This method does not need to be called in the normal use of QUnit.
- QUnit.reset( )
- Automatically called by QUnit
Assertions
- ok( state, message )
- A boolean assertion, equivalent to JUnit’s assertTrue. Passes if the first argument is truthy.
- equal( actual, expected, message )
- A comparison assertion, equivalent to JUnit’s assertEquals.
- notEqual( actual, expected, message )
- A comparison assertion, equivalent to JUnit’s assertNotEquals.
- deepEqual( actual, expected, message )
- A deep recursive comparison assertion, working on primitive types, arrays and objects.
- notDeepEqual( actual, expected, message )
- A deep recursive comparison assertion, working on primitive types, arrays and objects, with the result inverted, passing when some property isn’t equal.
- strictEqual( actual, expected, message )
- A stricter comparison assertion then equal.
- notStrictEqual( actual, expected, message )
- A stricter comparison assertion then notEqual.
- raises( block, expected, message )
- Assertion to test if a callback throws an exception when run.
Asynchronous Testing
- start( decrement )
- Start running tests again after the testrunner was stopped. See stop().
- stop( increment )
- Stop the testrunner to wait for async tests to run. Call start() to continue.
仕様に関してはこんな感じなんですが、実際に触ったものを見ないと分かりづらいと思うので、次項で、簡単なプログラムを元に説明したいと思います。
3.実際に触ってみる
まず、解凍して一番最初のディレクトリにjsと言うフォルダを作成しました。ここに実際に開発するjsファイルを保存します。
今回はmath.jsと言うファイルを作成しました。
function math(x){ return x*x; };
引数xで受け取った値を2乗して結果を返す単純なプログラムです。
次にこの作成したプログラムをテストするファイルを作成します。
トップディレクトリにあるtestディレクトリの中にtest-math.jsというファイルを作成します。
test('2乗計算式', function(){ equal(math(2), 4);//引数2を渡し、結果が4と想定 same(math(2), 6);//引数2を渡し、結果が6と想定 });
実際関数に対して与えたい引数を渡し、想定した結果を記述します。
equalというAPIを利用していますが、これは第一引数と第二引数がイコールかと言う判定です。
上記で、実際の開発用ファイルと、テスト用ファイルが完成したので、動作を確認します。
動作確認はブラウザでの検証になるので、htmlファイルに記述します。
今回はtestフォルダに入っている、index.htmlファイルを修正して利用しました。
QUnit Test Suite <script type="text/javascript" src="../qunit/qunit.js"></script><script type="text/javascript" src="../js/math.js"></script> <script type="text/javascript" src="test-math.js"></script></pre> <div id="qunit"></div> <div id="qunit-fixture">test markup</div> <pre>
上記のhead部分で、各ファイルを読み込む順番は正しく設定して下さい。
- 1.QUnit(qunit.js)
- 2.開発用ファイル(math.js)
- 3,テスト用ファイル(test-math.js)
実際の画面
test-math.jsにて2つの記述をしていました。その為、実行結果の方でそれぞれの結果が表示されます。
1番目の方は、渡した引数と想定した結果が正しい値な為、緑色の文字でOKと表記されています。
一方2つ目の記述に関しては渡した引数が1つ目のものと同じなのに、想定している結果が異なっています。これは当然誤った値になるので、赤色で間違っているという結果を表記しています。
Expend: 6というのは、equal関数で第二引数に渡した「6」と言う値です。想定した計算結果が間違っていた数字です。
Resoltという項目で算出された結果である「4」が帰ってきています。
Diffの項目で6が誤っており、正しくは4であると記述までしています。
このように、ある程度の値と結果を想定しておき、プログラムが正常に動作するか、予測通りに動作しないかというテストを行うことが出来ます。
4.クリックイベントで確かめてみる
さっきの項目では、演算においてのテストでしたが、これ以外にjQueryを利用してもテストしてみました。
サンプルとして、クリックした要素をmargin-leftで右側へ動かすようにし、親要素に当たるdivのボックスをはみ出す場合は動作させないようにしました。
</pre> <div id="testblock"></div> <pre>
#testblock{ width:500px; height:50px; padding:10px; background:#ccc; } #mouse_event{ width:30px; height:30px; background:#f00; display:block; }
開発用jsファイルを上記と同様に、jsフォルダに設置します。
function move_r(data){ var move_data = data; var parent_data = parseInt($("#mouse_event").parent('div').css('width')); //parseIntを利用して親要素の幅の値を取得 if(parent_data >= move_data + 30){//受け取った引数と動かす対象の幅(30)を加算し、親要素の幅を超えなければアニメーションを実行 $("#mouse_event").animate({ 'margin-left':move_data }); return true; }else{ return false; } } module("DOM操作"); test("move_r", function(){ equal(move_r(470),true,"OK"); equal(move_r(470),false,"NO"); equal(move_r(5000),false,"NO"); });
他にも書き方は色々ありますが、僕はシンプルにif文分岐を使い、アニメーションすれば”true”を返し、そうでなければ”false”を返すようにし、テストさせました。
5.最後に
上記で述べたとおり、APIはequal意外にもありますし、他のサイトでも色々参考になるものが多数有りました。ただ、参考にしたサイトと言っても数年前の情報だったりもします。現在では廃止になったAPIもありますので、参考にされる際は自己責任のもとご活用下さい。
参考にさせていただいたサイト等
- テスト駆動開発:wikipedia
- http://docs.jquery.com/QUnit
- QUnitの基本的な使い方 – Block Rockin’ Codes
- javascript、いや、jQueryのテストツールQUnit使ってみた – しかじろうがプログラム作るよ!
- 今から3分で qUnit の使い方を身に付ける (JavaScriptの単体テスト)
- javascriptのテストのはなし:QUnit | Classmethod.dev()
- 【Javascript】QUnit導入と単体テスト 第1回社内勉強会:うのらぼ。
Comments