Yaks

script.aculo.usのUnitTestの使い方 前編 はてなブックマーク - script.aculo.usのUnitTestの使い方 前編

Javascript用のテストライブラリを探していたのですが、そういえば script.aculo.usについてるじゃん、と思い見てみるとなんか使えそう。
じゃあもう少し詳しく調べて・・・みようと思ったら、資料があんまりない。公式サイトのドキュメントもめっちゃ書きかけ。
しょうがないので、結局 script.aculo.us自身のテストを読むことに・・・

というわけで、せっかくなので UnitTestの使い方の解説をしてみようと思います。なんかここんとこ技術系の話題に向きが振れ気味。そしてまた 2回にわかれます・・・

1.用意するもの

とりあえず script.aculo.usの一部なので、ベースとなるprototype.jsは必須です。
script.aculo.usに最新の prototype.jsが同梱されているので、それを使えばよいでしょう。 というわけで、prototype.jsを使ってない(使いたくない)場合にはやや不向きかもしれません。

あとは、テストを書く HTMLを用意します。テストなのでとりあえず HTMLに直に Javascriptを埋め込む形で書いていきます。

雛型はこんな感じになります。(この辺は、script.aculo.usのテストからコピペ。ただちょっとHTMLは簡略化してます。)

<html>
<head>
<script type="text/javascript" src="prototype.js"></script>
<script type="text/javascript" src="scriptaculous.js?load=unittest"></script>
<link rel="stylesheet" href="test.css" type="text/css" />
</head>
<body>


<script type="text/javascript" language="javascript">
<!-- ここにテストケースを書く -->
</script>

</body>
</html>

とりあえず、script.aculo.usは最低限必要な unittest.jsだけをインクルードしていますが、effectや dragdropなんかも必要に応じて追加して下さい。メンドウならフルインクルードで。(言うまでもなく、パスは適当なものに置き換えてくださいませ。)
test.cssは、script.aculo.usのテスト(testフォルダ)に含まれているものです。自分で書いたり、別に使わなくてもよいのですが、楽チンで見やすくなりますし、テストは別に一般公開するものでもないので(今ここでしてるけど。)拝借しちゃいましょう。

2.テスト用の HTMLタグ

次に、scriptタグの前に、テスト内(テスト対象のコード)で操作する HTMLタグと、結果の出力用の 'testlog'という idを持った要素(divがよいでしょう)を追加します。(ID名は変更できます。) 特に必要がなければ、結果の出力用の要素だけで構いません。

<html>
<head>
<script type="text/javascript" src="prototype.js"></script>
<script type="text/javascript" src="scriptaculous.js?load=unittest"></script>
<link rel="stylesheet" href="test.css" type="text/css" />
</head>
<body>

<!-- テスト結果の出力 -->

<div id="testlog"></div>

<!-- なんかDOM操作用 -->

<div id="test"></div>


<script type="text/javascript" language="javascript">
<!-- ここにテストケースを書く -->
</script>

</body>
</html>

3.テストケースの記述

あとは、scriptの中にずらずらとテストケースを書いていきます。

基本的には Test.Unit.Runnerのコンストラクタの引数に testXXという名前の関数を連ねたオブジェクトを渡してやると、それがテストケースに・・・って文章で書いてもわかりにくいので、実際のソースをご覧下さい。(前後のHTMLタグは省略してます。)

<script type="text/javascript" language="javascript">
<!-- 

	new Test.Unit.Runner( {

		//テストケースは 'test' から始まる 関数名で定義
		testSample: function(){ with( this ) {
			
			// setup、teardownという関数を用意しておくと、それぞれ各テストケースの実行前と後に呼び出されます。
			// ようはテストの前処理と後処理。ちなみにこれは必須ではありませんので、必要に応じて。
			
			setup: function(){ with( this ){
				// テストケースの前処理
			} },

			teardown: function(){ with( this ){
				// テストケースの後処理
			} },

			testCase1: function(){ with( this ) {
				var o = new Object();
				assertInstanceOf( Object, o );
			} },

			testCase2: function(){ with( this ) {
				var i = 1;
				assertEqual( 2, ++i );
			} }
		} },

		// こんな感じで並べて書いていきます。
		testSample2: function(){ with( this ) {

		} }
		
	} /* , { testLog: [output target ID] } ←テストの出力先の idを変えたい場合はここに指定  */ );

// -->
</script>

なんかテストの中身がいい加減すぎますが、とりあえず外側だけ見てください。 testCase1、testCase2がそれぞれ Case1、Case2というテストケースになり、結果が先ほどの testlogタグに出力されます。 この辺の記述は JUnitなんかとおんなじ感じです。
ちなみに with(this)はおまじない・・・じゃだめですね、assert系関数を持った Test.Unit.Assersionクラスをテストケースに継承(Object.extend)させているので、これを呼び出すのに必要です。

ちなみに結果は(先ほどのCSSを使っていると)こんな感じになります。

result.png
(画像で横着・・・)

あとは、以下に挙げる assert系関数でテストを書いていきます。

4.テスト関数 簡易リファレンス

assert( A[, message] )
Aが trueになるかどうか。
assertEqual( B, A[, message] )、assertNotEqual( B, A[, message] )
Aの値が Bの値に等しいかどうか。または等しくないかどうか。
assertInspect( B, A[, message] )
A.inspect()の結果が、Bに等しいかどうか。
assertEnumEqual( B, A[, message] )
Enumerableインターフェイスを持つ AとBの要素が全て等しいかどうか。
assertIdentical( B, A[, message] )、assertNotIdentical( B, A[, message] )
BがAと型、値ともに等しい(===)かどうか。または等しくないかどうか。
assertNull( A[, message] )、assertNull( A[, message] )
Aが nullかどうか、または nullではないかどうか。
assertMatch( regex, A[, message] )
Aが regexの正規表現にマッチするかどうか。
assertType( B, A[, message] )、assertNotOfType( B, A[, message] )
Aの型が Bと一致するかどうか、一致しないかどうか。
assertHidden( element[, message] )
element (HTML要素)が非表示 (display: none)かどうか。
assertInstanceOf( B, A[, message] )、assertNotInstanceOf( B, A[, message] )
Aが Bのインスタンスかどうか、インスタンスではないかどうか。
assertRespondsTo( method, Obj[, message] )
Objが 関数 methodを持っているかどうか。
assertReturnsTrue( method, Obj[, message] )、assertReturnsFalse( method, Obj[, message] )
Obj.method()関数が true、または falseを返すかどうか。(method()が見つからない時は is[Method]()も探す?ここはソースで見ただけ )
assertRaise( exception, func[, message] )
関数 func()が 例外 exception(例外名)を 発生させるかどうか。exceptionが nullの場合は、例外が発生するかどうかのみチェック。
assertElementsMatch( elements, selector1, selector2, ... , selectorN [, message] )
配列 elementsの 各HTML要素が 対応する後続の引数で表すCSSセレクタにマッチするか。(Element.match()関数を使用。) (分かりにくいですが、$A( elements ).all( function(item, index){ $(item).match( arguments[index+1] ) } ); てなことをして、全部 trueになるかを調べます。これもわかりにくいか...)
assertElementMatchs( element, selectos [, message] )
element (HTML要素)が、配列 selectorsの各要素で指定した CSSセレクタに全てマッチするかどうか。(上の変形。1つの HTML要素に対して、複数パターンで matchをかける感じ。)
assertVisible( element[, message] )、assertNotVisible( element[, message] )
element(HTML要素)が可視状態かどうか。または不可視状態かどうか。 (assertHiddenが、その要素自体の表示・非表示の状態だけを見るのに対し、こちらは親要素の表示・非表示もチェックする。)

それぞれの [message]引数は、失敗(Failed)した場合に、テストログに表示させるメッセージです。 省略するとデフォルトのメッセージが表示されますが、 assert()とか一部の関数は、複数あった場合にどれが失敗したか分かりにくくなったりするので、必要に応じて指定しておくとよいでしょう。、

とりあえず、ここで挙げた関数を一通り呼び出しているサンプルを載せておきます。(ちなみに、わざと 例外を起こさせているところがあるので、firebugなんかでひっかかると思いますがご了承くださいませ。)

ユニットテスト サンプル

実際のソースを見ると、どんな感じで使うのかイメージがわくかと思います。。

長くなったので waitや benchmark、Event.simulateXX系は次回に。

であ、また。