前回に引き続いて、unittest.jsを解説します。豊富に用意されているアサートの関数名は、JUnitを使った経験があるかたには、なじみのあるものだと思います。
Test.Unit.Assertions
Test.Unit.Assertionsは、テスト内部で使うアサートのクラスです。アサートの成功、失敗、エラーの数もこのクラスで管理します。
252~258行目のinitializeは、インスタンスの初期化をする関数です。assertionsはアサートの成功の数、failuresは失敗の数、errorsはエラーの数、messagesは、テストのメッセージをまとめるための配列です。
259~265行目のsummaryは、アサートの成功の数、失敗の数、エラーの数、メッセージをまとめた文字列を返す関数です。
266~268行目のpassは、アサートが成功したときに呼ばれる関数です。assertionsをインクリメントします。
269~272行目のfailは、アサートが失敗したときに呼ばれる関数です。failuresをインクリメントします。失敗のメッセージを追加します。
273~275行目のinfoは、メッセージを追加する関数です。
276~279行目のerrorは、アサートの実行中にエラーがあったときに呼ばれる関数です。errorsをインクリメントします。エラーメッセージを追加します。
280~284行目のstatusは、アサートの結果から、失敗がひとつでもあれば'failed'を、同様にエラーがあれば'error'を、何もなければ'passed'を返す関数です。
アサートのための関数群
285~290行目のassertは、与える式が必ずtrueを返すというアサートです。
291~297行目のassertEqualは、"予想の値"、"実際の値"として、それらが必ず等しいというアサートです。
298~304行目のassertInspectは、"予想のInspect"、"実際の値"として、そのインスペクトが必ず等しいというアサートです。
305~312行目のassertEnumEqualは、"予想の配列"、"実際の配列"として、それらの配列の内容が必ず等しいというアサートです。
313~318行目のassertNotEqualは、assertEqualの反対で、"予想の値"、"実際の値"として、それらが必ず等しくないというアサートです。
319~325行目のassertIdenticalは、assertEqualとほとんど同じですが、比較に===を使うところが違います。"予想の値"、"実際の値"として、それらが===で比較して必ず等しいというアサートです。
326~332行目のassertNotIdenticalは、assertIdenticalの反対で、"予想の値"、"実際の値"として、それらが===で比較して必ず等しくないというアサートです。
333~338行目のassertNullは、オブジェクトが必ずnullであるというアサートです。
339~345行目のassertMatchは、"正規表現の文字列"、"実際の文字列"として、その正規表現が必ずマッチするというアサートです。
346~349行目のassertHiddenは、要素が必ず非表示であるというアサートです。要素のCSSのdisplay属性が必ず"none"と等しいとします。
350~353行目のassertNotNullは、assertNullの反対で、オブジェクトが必ずnullでないというアサートです。
354~361行目のassertTypeは、"予想のコンストラクタ"、"実際の値"として、必ずそのコンストラクタの値であるというアサートです(Core JavaScript 1.5 Reference:Object:constructor)。
362~369行目のassertNotOfTypeは、assertTypeの反対で、"予想のコンストラクタ"、"実際の値"として、必ずそのコンストラクタの値でないというアサートです。
370~376行目のassertInstanceOfは、"予想の型"、"実際の値"として、必ずその型の値であるというアサートです。
377~383行目のassertNotInstanceOfは、assertInstanceOfの反対で、"予想の型"、"実際の値"として、必ずその型の値でないというアサートです。
384~390行目のassertRespondsToは、"メソッド名"、"オブジェクト"として、そのメソッドが必ずあるというアサートです。
391~399行目のassertReturnsTrueは、"メソッド名"、"オブジェクト"として、そのメソッドを呼ぶと必ずtrueを返すというアサートです。
400~408行目のassertReturnsFalseは、assertReturnsTrueの反対で、"メソッド名"、"オブジェクト"として、そのメソッドを呼ぶと必ずfalseを返すというアサートです。
409~417行目のassertRaiseは、"例外の名前"(nullでも構いません)、"関数"として、その関数を呼ぶと必ずその名前の例外が発生するというアサートです。nullを指定したときは、とにかく例外が発生すれば成立します。
418~429行目のassertElementsMatchは、1番めの引数に要素の配列を、残りの引数に、その配列の長さと同じだけのCSSセレクタの文字列をとって、それぞれの要素がそれぞれのCSSセレクタにElement.matchで必ずマッチするというアサートです。
430~432行目のassertElementMatchesは、assertElementsMatchの単数形です。引数に要素とCSSセレクタの文字列をとって、その要素がそのCSSセレクタにElement.matchで必ずマッチするというアサートです。
433~440行目のbenchmarkは、処理の実行にかかる時間を計測する関数です。1番めの引数の処理を、2番めの引数の回数(デフォルトは1回)だけ繰り返して、それにかかったミリ秒数を返します。3番めの引数に処理の名前を渡すことができます。
437行目で、infoメソッドで、人間に読みやすいメッセージを追加します。
441~449行目の_isVisibleは、要素が見える状態かどうかを判定する関数です。assertVisibleとassertNotVisibleの内部で使われます。要素自身のCSSのdisplay属性が'none'でなくても、その親要素が'none'なら結局見えないことになりますから、それも含めてチェックします。
448行目で、再帰で親要素をたどります。
450~452行目のassertNotVisibleは、要素が必ず見えない状態であるというアサートです。上述の_isVisibleを使います。そのため、assertHiddenと違って、親要素の状態に左右されます。
453~455行目のassertVisibleは、assertNotVisibleの反対で、要素が必ず見える状態であるというアサートです。上述の_isVisibleを使います。そのため、親要素もすべて見える状態である必要があります。
benchmarkが2回、まったく同じ内容で定義されています。ミスでしょう。
Test.Unit.Testcase
Test.Unit.Testcaseは、テストの実行についての情報を扱うクラスです。Test.Unit.Assertionsを継承します。
468~486行目のinitializeは、インスタンスの初期化をする関数です。引数にテスト名、テスト内容(関数か、その中身を記述した文字列)、テスト前に実行する処理、テスト後に実行する処理をとります。
469行目で、継承元のTest.Unit.Assertionsのinitializeを呼びます。
470行目の、this.nameはテスト名です。
472~477行目で、テスト内容を文字列で与えた場合を扱います。
473、474行目は、BDDスタイルの特殊な記述を扱うための正規表現置換のようですが、これについては全く文書が見つからないので、解説を控えます。
475行目で、テスト内容の文字列をevalで解釈します。
482行目のsetupは、テスト前に実行する処理です。
483行目のteardownは、テスト後に実行する処理です。
484行目のisWaitingは、テストの内部でwait関数が呼ばれたときにtrueになるフラグで、テストの実行を待っている状態であることを表します。
485行目のtimeToWaitは、テストの内部でwait関数が呼ばれたときに設定される、待ち時間です。
487~491行目のwaitは、テストの実行を指定時間だけ待つ関数です。引数に時間と、続きのテスト処理をとります。この処理は、Test.Unit.RunnerクラスのrunTestsも絡むので少々複雑です。
489行目で、テスト内容をnextPartに置き換えているのがキモです。指定時間後のrunTestsで、nextPartが実行されるというわけです。
492~506行目のrunは、テストを実行する関数です。isWaitingが絡んでいるので、理解しづらいと思います。まず、テストの初回のrunの呼び出し時には、isWaitingは必ずfalseです。そこでsetupが呼ばれます。次にtestが呼ばれます。testの内部でwait関数を使っていなければ、isWaitingはそのままfalseで進むので、teardownが呼ばれて、終了です。testの内部でwait関数を使うと、isWaitingがtrueになります。teardownを呼ばずにこのrun関数を抜けて、Test.Unit.RunnerのrunTests関数に戻ります。そこで再び、wait関数で渡したnextPartが指定時間後にrunで呼ばれる仕組みになっています。
495行目で、isWaitingを見ることで、初回のrunの呼び出しかわかります。その場合はsetupを呼びます。
497行目で、テストを実行します。test関数をthisにバインドしてから呼びます。
498行目で、テストの終了後の処理をします。isWaitingを見ることで、内部でwait関数を呼ばなかったことがわかります。その場合はteardownを呼びます。
504行目で、テストの実行中にエラーがあった場合は、errorを呼びます。
BDDスタイルのテスト記述
BDDはビヘイビア駆動開発の略で、このライブラリでは以下のように記述します。JavaScript自体の制限から、RubyのRSpecほどエレガントでないところもありますが、これまでより読みやすいのが特徴です。こちらにRSpecの解説記事があります。
それではコードに戻りましょう。
508~543行目のTest.setupBDDExtensionMethodsは、BDDスタイルのテスト記述を実現するための関数です。RSpecから多くの着想を得ています。
512~526行目は、BDDスタイルのメソッド名と、Test.Unit.Assertionのメソッド名の対応付けです。
527行目のmakeAssertionは、assertionにTest.Unit.Assertionのメソッド名を、argsにBDDスタイルのメソッドの引数、objectにメソッドを呼んだオブジェクトを取って、実際のTest.Unit.Assertionの関数をapplyで呼ぶ関数です。
531行目で、BDDのメソッドを全て挙げるためのハッシュテーブルを用意します。
532~537行目で、BDDスタイルのメソッドの中身を作ります。実体は、BDDスタイルのメソッドの呼び出し元のオブジェクトと、その引数の順序を組み換えて、Test.Unit.Assertionの関数を呼ぶようになっています。
539~541行目で、BDDスタイルになるように、配列、文字列、数字、ブール値のそれぞれにメソッドを追加します。
544~568行目のTest.contextは、これまでTest.Unit.Runnerで実行していたテストを、BDDスタイルに書くための関数です。引数のnameにテスト全体の名前を、specにテストの記述を、logにログを出力する要素のDOM idを取ります。
545行目で、前述のsetupBDDExtensionMethodsを呼んで準備をします。
551、552行目で、setup、teardownの処理をspecから取りだします。
555行目以降が本質的な部分です。
556行目で、テスト名をBDDスタイルからJavaScript風に変換します。具体的には、'should automatically add extensions to strings'が'testShouldAutomaticallyAddExtensionsToStrings'になります。
557行目の、bodyとは、関数の中身のことを指します。関数をtoStringで文字列にして、splitで文字列の配列にしてから、このような処理をして関数の中身を取り出します。
はじめの状態です。
557行目のslice(1)で、最初の'function()'を取り除きます。
558行目のslice(1)で、最初の'{'を取り除きます。
この処理は'function(){'の書法ならば必要ないので、正規表現でチェックしています。
559行目のpop()で、最後の'}'を取り除きます。
560行目のstatement.strip()で、全体から空白を取り除きます。
563行目で、こうして得られた結果の、文字列の配列を結合し、文字列にして完了です。後で、Test.Unit.Runnerの内部でevalを呼んで、関数に解釈します。
564行目で、テストのタイトルを設定します。
567行目で、これらをTest.Unit.Runnerに渡します。