Backbone の View をテストする

以前 Konacha を導入してから、 Rails アプリの JavaScript コードをテストできるようになった。

JavaScript コードの大半は、Backbone を使って実装した UI なんだけど、 困ったことに View のテストをほとんど書けていない。 比較的テストが書きやすい Model は、がっちりテスト書いてある。 なんとか View も単体テストできないものか。

View のテスト方法を模索している最中、Konacha を導入して使えるようになるアサーションライブラリ Chai の、 Chai jQuery というプラグインを知った。

Chai jQuery プラグインを使えば View のテストが書けそうだな。早速試してみた。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Testing Backbone View</title>
    <link rel="stylesheet" href="mocha.css" />
  </head>
  <body>
    <!--mocha のテストランナーが表示される要素-->
    <div id="mocha"></div>

    <!--FormView が使うテンプレート-->
    <script id="form_template" type="text/template">
      <div class="control-group">
        <label for="name">Name</label>
        <div class="controls">
          <input name="name" type="text" value="<%= name %>"/>
        </div>
      </div>
      <div class="control-group">
        <label for="completed">Completed</label>
        <div class="controls">
          <input name="completed" type="checkbox" <% if(completed) { %>checked<% } %>/>
        </div>
      </div>
      <div class="form-actions">
        <input type="submit" class="btn btn-primary" />
      </div>
    </script>

    <!--mocha のセットアップ-->
    <script src="jquery.js"></script>
    <script src="mocha.js"></script>
    <script>mocha.setup('bdd')</script>

    <!--chai と chai プラグインを読み込む-->
    <script src="chai.js"></script>
    <script src="chai-jquery.js"></script>

    <!--テスト対象の View を定義-->
    <script src="underscore.js"></script>
    <script src="backbone.js"></script>
    <script type="text/javascript">
    var Task = Backbone.Model.extend({
      defaults: {
        name: "",
        completed: false
      }
    });

    var FormView = Backbone.View.extend({
      template: _.template($("#form_template").html()),

      initialize: function() {
        this.model.on("sync", this.render, this);
      },

      render: function() {
        var ctx = this.model.toJSON();
        var html = this.template(ctx);
        this.$el.html(html);
        return this;
      }
    });
    </script>

    <!--mocha+chai で Backbone の View のスペックを書く-->
    <script type="text/javascript">
    describe("FormView", function() {
      describe("render", function() {
        var view, model;

        beforeEach(function() {
          model = new Task({ name: "foo", completed: true });
          view = new FormView({ model: model });
        });

        it("自身を返す", function() {
          var result = view.render();
          chai.expect(result).to.eq(view);
        });

        it("DOM を構築する", function() {
          view.render();
          chai.expect(view.$el).to.have("input.btn.btn-primary[type=submit]");
          chai.expect(view.$el).to.have("input[name=name][type=text][value=foo]");
          chai.expect(view.$el).to.have("input:checked[type=checkbox][name=completed]");
        });
      });
    });
    </script>

    <!--mocha のテストランナー実行-->
    <script>
      mocha.checkLeaks();
      mocha.globals(['jQuery']);
      mocha.run();
    </script>
  </body>
</html>

開発中はしょっちゅうテンプレートが変わるので、厳密なチェックはやらずに、セレクタでチェックするくらいでいい。

上記のサンプルでは HTML に直接書いたけど、この方法だと本番用テンプレートを使ってテストできないので、 ビューの中にテンプレートを移動するか、外部ファイルにするべきだったな。

まぁ、Rails アプリなら rails-backbone を使えば、テンプレートは外部ファイルで作成するから問題ないはず。 ただ、いずれは Bower で Backbone 入れたいんだけど、その場合テンプレートどうすればいいんだろうな。