0-9
JsTestDriverのAsyncTestCase

この内容はJsTestDriver WikiのAsyncTestCaseを意訳したものではありません。
http://code.google.com/p/js-test-driver/wiki/AsyncTestCase

TestCaseはAsyncTestCaseの同期実行版なので基本的にAsyncTestCaseを使いましょう。
(TestCaseをAsyncTestCaseに置き換えるだけでそのまま実行できます)

assert関係はこちらからどうぞ
Assertions http://code.google.com/p/js-test-driver/wiki/Assertions

以下の二つはTestCaseと互換の基本的な実行方法です。
オブジェクトリテラルを渡す方法
    AsyncTestCase(‘testCaseName’, {
        ‘setUp’ : function () {},
        ‘testNameA’ : function () {},
        ‘testNameB’ : function () {},
        ‘tearDown’ : function () {}
    });
.prototypeを拡張する方法
    var MyTestCaseName = AsyncTestCase(‘testCaseName’);
    MyTestCaseName.prototype.setUp = function () {};
    MyTestCaseName.prototype.testNameA = function () {};
    MyTestCaseName.prototype.testNameB = function () {};
    MyTestCaseName.prototype.tearDown = function () {};
両者に大きな違いはありませんが、特にオブジェクトリテラルを渡す場合ではテスト名に記号等を使用するとコマンドラインからテストを指定して実行する場合に問題が発生する可能性があるので注意してください。
(コマンドラインから指定しない場合、スペース等の記号を含めても問題ありません)
JsTestDriverのコマンドラインオプション http://0-9.tumblr.com/post/16860918312/jstestdriver-commandlineflags
テスト名は常に「test」から始まっている必要があります。
(setUp、tearDown以外の「test」から始まっていないメソッドは実行されません)
ただし、「test」から始まっていないメソッドも「this.メソッド名」で呼び出すことが可能です。
各テストメソッドは毎回以下のサイクルで実行されます。
    setUp -> テストメソッド -> tearDown
各テストメソッドはアルファベット順にソートされて実行されるため、上記の例の場合以下の順番で実行されます。
    setUp -> testNameA -> tearDown -> setUp -> testNameB -> tearDown
各メソッド内のthisは共有されており、「setUp -> テストメソッド -> tearDown」の実行サイクル中以下のように保持されます。
    setUp -> テストメソッド -> tearDown -> (thisを破棄) -> setUp -> テストメソッド -> tearDown

以下はAsyncTestCase独自の方法です。

queueを使った非同期テスト
    AsyncTestCase(‘testCaseName’, {
        ’testNameHoge’ : function (queue) {
            var state = 0;
            assertEquals(0, state);
            queue.call(function(callbacks) {
                state++;
                assertEquals(1, state);
                setTimeout(callbacks.add(function () {
                    state++;
                    assertEquals(2, state);
                }), 500);
            });
            queue.call(function(callbacks) {
                state++;
                assertEquals(3, state);
                setTimeout(callbacks.add(function () {
                    state++;
                    assertEquals(4, state);
                }), 200);
            });
        }
    });

AsyncTestCaseはテストメソッドの第一引数にqueueオブジェクトを渡すので、そのqueueオブジェクトの.callメソッドにcallbackをわたし、そのcallbackの第一引数に渡されるcallbacksオブジェクトの.addメソッドにcallbackを渡すことで非同期の実行ができます。

実行順は上記のテストにもありますが、以下のような順番になります。

テストメソッド全体 -> (遅延) -> queue.call -> callbacks.add -> queue.call -> callbacks.add

各queue.call内のcallbackは遅延実行され、先に追加されているqueue.call内のcallbacks.addが全て処理されるまで実行されません。
    AsyncTestCase(‘testCaseName’, {
        ’testNameHoge’ : function (queue) {
            var state = 0;
            assertEquals(0, state);
            queue.call(function(callbacks) {
                state++;
                assertEquals(1, state);
            });
            assertEquals(0, state);
        }
    });

テストメソッド全体、queue.call、callbacks.add内のthisはすべて同じものとなります。
    AsyncTestCase(‘testCaseName’, {
        ’testNameHoge’ : function (queue) {
            assertNotEquals(1, this.hoge);
            this.hoge = 1;
            queue.call(function(callbacks) {
                assertEquals(1, this.hoge);
                setTimeout(callbacks.add(function () {
                    assertEquals(1, this.hoge);
                }));
            });
        }
    });

htmlのテスト

JsTestDriverのテストはサーバに接続しているブラウザ環境上で実行されるため、通常のDOM APIを使ったhtmlの組み立ても可能ですが、各テストコード内に以下のようなJavaScriptのコメント形式でhtmlを記述することにより簡単にテスト用htmlを埋め込むことができます。
    AsyncTestCase(‘testCaseName’, {
        ‘setUp’ : function () {
            /*:DOC foo = <div><p>foo</p></div>*/
            /*:DOC += <div id=”foo”></div> */
        },
        ‘testNameHoge’ : function () {
            /*:DOC foo = <div><p>foo</p></div>*/
            /*:DOC += <div id=”foo”></div> */
        }
    });
setUp内で設定したhtmlは各テストメソッドから参照可能です。
(ただし、htmlへの変更はtearDown実行後に破棄されます)
テストメソッド内で設定したhtmlはtearDown実行後に破棄されます。
:DOCの後に変数名を置く形式「:DOC foo = 」の場合、各コード内からはthis.fooで参照できます。
:DOCの後に+=を置く形式「:DOC += 」の場合、document.bodyにappendChildされます。
各htmlは要素を並列においても最初のノード以外を無視します。
以下の例ではfooのみが追加され、barは無視されます)
    /*:DOC +=
        <div id=”foo”></div>
        <div id=”bar”></div>
    */
各コードは記述されている部分がhtmlを生成するJSに変換されて実行されるため、テストメソッドの任意の行に記述できますが、記述された行以前からは参照できません。
    AsyncTestCase(‘testCaseName’, {
        ‘testNameHoge’ : function () {
            // この時点ではundefined
            console.log(this.foo);
            /*:DOC foo = <div><p>foo</p></div>*/
            // ここでは[HTML Element]
            console.log(this.foo);
        }
    });


注意点

クライアントに限って、延々サーバに繋げてると動きがおかしくなることがあります。
(社内では共有のサーバにiPhone, Androidをつなげて放置してますが、週一回か二回くらい再起動してます)

console.logはブラウザ上に存在しない場合もあるし、つかいまくるとブラウザが不安定になったりするので、コンソールに値出したいだけならjstestdriver.console.logを使いましょう。
(ただし、console.log = jstestdriver.console.logとかしても動かないので注意)

一応debugger statementも動きます。ただし、timeout秒以上経つとそこで死ぬので注意。
(timeout秒以内ならstartしなおせば動く)

TestCase、AsyncTestCase外の実行場所はテストを再実行した場合には再実行されません。
(初期化コードとかは基本setUpに書きましょう)

  1. joodle0-9からリブログしました
  2. atm09td0-9からリブログしました
  3. 0-9の投稿です