Secret of the JavaScript Ninja 2章を読んだ

d:id:ojimacと定期的に読んでいくことになった。
一緒に読みたい!という人がいれば気軽にTwitter(@cheesepie)で声かけてください!


2章はデバッグの基本(ロギングとブレークポイント)とテストケースについて。

ロギングはまあconsole.logをクロスブラウザ化して使いましょう、ブレークポイントはブラウザのコンソール使ってやっていきましょうてな具合で。


テストケースは3つのポリシーを守るべし。

  • テストは高い再現性を持つべし
  • テストは出来る限りシンプルにするべし
  • テストはテスト同士が依存しないように最小単位のテストに分割するべし


単体テストフレームワークとして、QUnit/YUITest/JSUnitが紹介されています。
JSUnitはメンテされてないので、モダンブラウザでのテストにはちょっと不安が。
個人的にはGoogle Closure Libraryのテストフレームワークが一番好みです!


で、既存のテストフレームワークを使った方がよさげなんだけど、どういう動きをしているのかを理解しておくのはいいことだよね。
ということで、テストフレームワークの基本要素となるAssertion/Test Groups/Asynchronous testingの3つがどういう仕組みになっているかをシンプルなコード例とともに説明しています。
それぞれ40行くらいで書けちゃうんだぜとはResig殿の弁。

非同期テストのサンプルが分かりやすかった。

      (function() {
        var queue = [], paused = false, results;

        this.test = function(name, fn) {
          queue.push(function() {
            results = document.getElementById("results");
            results = assert( true, name ).appendChild(
              document.createElement("ul") );
            fn();
          });
          runTest();
        };

        this.pause = function() {
          paused = true;
        };

        this.resume = function() {
          paused = false;
          setTimeout(runTest, 1);
        };

        function runTest() {
          if ( !paused && queue.length ) {
            queue.shift()();
            if ( !paused ) {
              resume();
            }
          }
        }

        this.assert = function assert( value, desc ) {
          var li = document.createElement("li");
          li.className = value ? "pass" : "fail";
          li.appendChild( document.createTextNode( desc ) );
          results.appendChild( li );
          if ( !value ) {
            li.parentNode.parentNode.className = "fail";
          }
          return li;
        };
      })();

      window.onload = function() {
        test("Async Test #1", function() {
          pause();
          setTimeout(function() {
            assert( true, "First test completed" );
            resume();
          }, 1000);
        });

        test("Async Test #2", function() {
          pause();
          setTimeout(function() {
            assert( true, "Second test completed" );
            resume();
          }, 1000);
        });
      };

このテストがどういう流れで処理されるかというと・・・

  1. Async Test #1のtest()が実行される
  2. キューにAsync Test #1のtest()の引数で渡された関数が入る
  3. runTest()
  4. キューから先ほど入れられた関数を取り出し実行する
  5. pausedをtrueにする
  6. Async Test #2のtest()が実行される
  7. キューにAsync Test #2のtest()の引数で渡された関数が入る
  8. runTest()
  9. !pausedがfalseを返すので何もしない
  10. Async Test #1のsetTimeoutで指定された関数が実行される
  11. "First test completed"をHTMLに書き出す
  12. pausedをfalseにし、runTest()
  13. キューからAsync Test #2のtest()の引数で渡された関数を取り出し実行する
  14. pausedをtrueにする
  15. Async Test #2のsetTimeoutで指定された関数が実行される
  16. "Second test completed"をHTMLに書き出す
  17. pausedをfalseにし、runTest()
  18. キューには何も入っていないので終了

キューとpause()とresume()を使うことで、非同期であっても必ず「#1のテスト→#2のテスト」という順番でテストが実行されるようになる。
これで3つのポリシーでも挙げられていた「テストは高い再現性を持つべし」を実現できるということか。


いやー勉強になります。
最後まで読んでみないと分かりませんが、Secret of the JavaScrpt Ninjaはサイ本と並んでオススメできるかも。
書籍の発売日は8月なのですが、PDF版は先行して購入できます。
PDF版もまだ14章までしかありませんが、章が追加されたり更新されるたびに新しいPDFを受け取れるようになっています。

Secrets of the JavaScript Ninja

Secrets of the JavaScript Ninja