JsTestDriverでテストコードを自作する

前回はJavaScript用のユニットテストフレームワークである「JsTestDriver」について、サンプルプロジェクトを用いてユニットテストを行う一連の手順を解説した。今回は自作のテストコードを動かしてみようと思う。

まずテスト対象となるJavaScriptコードだが、ここではショッピングカートを想定して以下のようなコードを用意した。このコードを記述したファイルを「ShoppingCart.js」としておく。

リスト1

var shop = { };

shop.Cart = function() {
        items = {};
};

// カートに商品を追加
shop.Cart.prototype.add = function(item, amount) {
        if (!items[item]) {
                items[item] = 0;
        }
        items[item] += amount;
};

// カートから商品を減らす
shop.Cart.prototype.remove = function(item, amount) {
        if (items[item]) {
                if (items[item] > 0) {
                        items[item] -= amount;
                }
    }
};

// カートから全ての商品を削除
shop.Cart.prototype.clear = function() {
        for (var item in items) {
                items[item] = 0;
        }
};

// カート内の特定の商品の個数を返す
shop.Cart.prototype.getAmount = function(item) {
        if (!items[item]) {
                items[item] = 0;
                }
        return items[item];
};

itemsはカートに入れられた商品の名前と個数を記録する。add()関数が呼び出されたら商品名をキーとしてitems内の個数をamountに指定された数だけ増やし、remove()関数が呼び出されたら逆に個数を減らす。clear()関数はカート内の全ての商品の個数を0にする。getAmount()は指定された商品の個数を返す関数である。なお、ここでは簡略化のため商品数の境界チェックは省略している。

これに対して、たとえばadd()関数をテストするためのテストコードは次のように書ける。

リスト2

ShoppingCartTest = TestCase("ShoppingCartTest");

// 商品追加のテスト
ShoppingCartTest.prototype.testAdd = function() {
          var cart = new shop.Cart();
        cart.add("hoge", 10);
        cart.add("hoge", 5);
          assertEquals(15, cart.getAmount("hoge"));
          jstestdriver.console.log("cart.getAmount(\"hoge\"): ", cart.getAmount("hoge"));
};

サンプルプロジェクトの場合と同様、TestCaseを用いて「ShoppingCartTest」というテストケースオブジェクトを作成し、それに対してShoppingCartTest.prototype.testAddというテスト用関数を定義している。このテスト関数ではカートに"hoge"という商品を追加し、その個数の合計が正しいかとうかをassertEquals()でチェックしている。

なおこのテストコードは、TestCase()の第2引数にテスト関数をインライン指定する方法で次のように書くこともできる。この方法で定義した場合でも実行結果は前述の例とまったく同じになる。

リスト3

TestCase("ShoppingCartTest", {
        // 商品追加のテスト
        testAdd:function() {
                  var cart = new shop.Cart();
                cart.add("hoge", 10);
                cart.add("hoge", 5);
                  assertEquals(15, cart.getAmount("hoge"));
                  jstestdriver.console.log("cart.getAmount(\"hoge\"): ", cart.getAmount("hoge"));
        }
});

同様に、remove()関数およびclear()関数用のテストコードを追加した例を以下に示す。このコードを記述したファイルを「ShoppingCartTest.js」とする。

リスト4

ShoppingCartTest = TestCase("ShoppingCartTest");

// 商品追加のテスト
ShoppingCartTest.prototype.testAdd = function() {
          var cart = new shop.Cart();
        cart.add("hoge", 10);
        cart.add("hoge", 5);
          assertEquals(15, cart.getAmount("hoge"));
          jstestdriver.console.log("cart.getAmount(\"hoge\"): ", cart.getAmount("hoge"));
};

// 商品削除のテスト
ShoppingCartTest.prototype.testRemove = function() {
          var cart = new shop.Cart();
        cart.add("piyo", 20);
        cart.remove("piyo", 10);
          assertEquals(10, cart.getAmount("piyo"));
          jstestdriver.console.log("cart.getAmount(\"piyo\"): ", cart.getAmount("piyo"));
};

// カートクリアのテスト
ShoppingCartTest.prototype.testClear = function() {
          var cart = new shop.Cart();
        cart.add("hoge", 10);
        cart.add("piyo", 20);
        cart.clear();
          assertEquals(0, cart.getAmount("hoge"));
          assertEquals(0, cart.getAmount("piyo"));
          jstestdriver.console.log("cart.getAmount(\"hoge\"): ", cart.getAmount("hoge"));
          jstestdriver.console.log("cart.getAmount(\"piyo\"): ", cart.getAmount("piyo"));
};

インライン関数で定義した場合は次のようになる。

リスト5

TestCase("ShoppingCartTest", {
        testAdd:function() {
                //......
        },
        testRemove:function() {
                //......
        },
        testClear:function() {
                //......
        }
});

以上でテスト対象のコードとテスト用のコードができた。これらのファイルの配置については、「jsTestDriver.conf」という名前の設定ファイルを用意し、そこに記述する。jsTestDriver.confの内容は以下のようになる。

リスト6

server: http://localhost:9876

load:
  - sources/*.js
  - tests/*.js

1行目はJsTestDriverのサーバを立ち上げるドメインとポートの設定である。この例ではlocalhostの9876番ポートを使用する設定になっている。そしてload:に続けて1行目にテスト対象のコードを、2行目にテスト用のコードをそれぞれ指定する。この例の場合、sourceディレクトリ以下の.jsファイルがテスト対象に、testsディレクトリ以下の.jsファイルがテスト用コードに指定されている。

したがって今回のショッピングカートの例では、JsTestDriverを実行するディレクトリから見て、sourceディレクトリにShoppingCart.jsを、testsディレクトリにShoppingCartTest.jsを配置すればよい。jsTestDriver.confはJsTestDriverの実行ディレクトリに置く。

では実際にテストを実行してみよう。JsTestDriver本体のバイナリファイルはこのページよりダウンロードすることができる。今回はJsTestDriver-1.0b.jarを使用する。まず次のように--portオプションに9876を指定してサーバを起動する。

プロンプト1

> java -jar JsTestDriver-1.0b.jar --port 9876

その状態でテスト環境として使用するWebブラウザから「http://localhost:9876/capture」にアクセスする。そして以下のようにテストを実行すればよい。この例ではFirefoxとGoogle Chromeの2つのブラウザでテストを行っている。

プロンプト2

> java -jar JsTestDriver-1.0b.jar --tests
all
......
Total 6 tests (Passed: 6; Fails: 0; Errors: 0) (3.00 ms)
  Chrome 3.0.195.6: Run 3 tests (Passed: 3; Fails: 0; Errors 0) (3.00 ms)
    ShoppingCartTest.testAdd passed (3.00 ms)
      [LOG] cart.getAmount("hoge"):  15
    ShoppingCartTest.testRemove passed (0.00 ms)
      [LOG] cart.getAmount("piyo"):  10
    ShoppingCartTest.testClear passed (0.00 ms)
      [LOG] cart.getAmount("hoge"):  0
      [LOG] cart.getAmount("piyo"):  0
  Firefox 1.9.0.13: Run 3 tests (Passed: 3; Fails: 0; Errors 0) (0.00 ms)
    ShoppingCartTest.testAdd passed (0.00 ms)
      [LOG] cart.getAmount("hoge"):  15
    ShoppingCartTest.testRemove passed (0.00 ms)
      [LOG] cart.getAmount("piyo"):  10
    ShoppingCartTest.testClear passed (0.00 ms)
      [LOG] cart.getAmount("hoge"):  0
      [LOG] cart.getAmount("piyo"):  0

このように、JsTestDriverを利用すればテスト対象となるアプリケーションのコード自体には変更を加えることなく、複数のブラウザの実行環境によるテストをまとめて実施することが可能となる。まだベータ版であり動作が安定しているとは言い難いので、本番環境で通用するレベルになるにはもうしばらく時間が必要だろうが、正式版のリリースが楽しみなツールだ。