Blogaomu

WEBアプリケーション開発とその周辺のメモをゆるふわに書いていきます。

Jasmine を使ってみる

こんにちは。今回は Jasmine という JavaScript のテストフレームワークを触ってみようと思います。 最近の WEB アプリケーションですと、JavaScript が担う役割はかなり大きくなっています。 非同期処理やMVCフレームワークなど複雑な処理を行うことも少なくありません。 こういう複雑な処理にはバグがつきもの、そこでテストコードの出番です。

以下調べたメモを書いていきます。間違いなどありましたらコメント等でご指摘頂けますと幸いです(^_^;)

スタンドアロン版をインストールしてみる

とりあえずは単純な JavaScript コードをテストしたいのでスタンドアロン版を使おうと思います。 Downloads · pivotal/jasmineから最新版をダウンロードして解凍。 解凍したものを適当なディレクトリにコピーします。こんな感じの構成。

/Users/takayuki_atkwsk/dev/javascript/study-jasmine
[takayuki_atkwsk]% tree
.
├── SpecRunner.html
├── lib
│   └── jasmine-1.3.1
│       ├── MIT.LICENSE
│       ├── jasmine-html.js
│       ├── jasmine.css
│       └── jasmine.js
├── spec
│   ├── PlayerSpec.js
│   └── SpecHelper.js
└── src
    ├── Player.js
    └── Song.js

4 directories, 9 files

大まかに言うと、SpecRunner.htmlがあってlibディレクトリには Jasmine ライブラリが入っています。 そして、srcディレクトリにはプロダクトコード、specディレクトリにはテストコードが置いてあります。

ダウンロードしたものには最初からデモコードが入ってますので、ブラウザでSpecRunner.htmlを開けばテストが実行されます。

RSpec風な結果が表示されるようです。 ちょっとSpecRunner.htmlの中身を見てみます。

<html>
<head>
  <!-- omitted -->

  <script type="text/javascript" src="lib/jasmine-1.3.1/jasmine.js"></script>
  <script type="text/javascript" src="lib/jasmine-1.3.1/jasmine-html.js"></script>

  <!-- include source files here... -->
  <script type="text/javascript" src="src/Player.js"></script>
  <script type="text/javascript" src="src/Song.js"></script>

  <!-- include spec files here... -->
  <script type="text/javascript" src="spec/SpecHelper.js"></script>
  <script type="text/javascript" src="spec/PlayerSpec.js"></script>

  <script type="text/javascript">
    (function() {
      var jasmineEnv = jasmine.getEnv();
      jasmineEnv.updateInterval = 1000;

      var htmlReporter = new jasmine.HtmlReporter();

      jasmineEnv.addReporter(htmlReporter);

      jasmineEnv.specFilter = function(spec) {
        return htmlReporter.specFilter(spec);
      };

      var currentWindowOnload = window.onload;

      window.onload = function() {
        if (currentWindowOnload) {
          currentWindowOnload();
        }
        execJasmine();
      };

      function execJasmine() {
        jasmineEnv.execute();
      }

    })();
  </script>
  <!-- omitted -->

<head>内でjasmine.js, jasmine-html.jsを読み込み、その後にプロダクトコード、テストコードの順で読み込んでいます。 次の<scirpt>タグでインラインコードが書かれています。window load で Jasmine を実行するように書かれていますね。

テストコード

次にテストコードを見てみましょう。spec/PlayerSpec.jsの最初の example のコードです。

describe("Player", function() {
  var player;
  var song;

  beforeEach(function() {
    player = new Player();
    song = new Song();
  });

  it("should be able to play a Song", function() {
    player.play(song);
    expect(player.currentlyPlayingSong).toEqual(song);

    //demonstrates use of custom matcher
    expect(player).toBePlaying(song);
  });

  // continue...
}

コードも RSpec風に書けるようです。普段 RSpec 使ってる人はあまり違和感なく書けそうですね。 まずdescribe()でテストスイートを定義します。引数は2つあって、最初はテスト対象の名前(オブジェクト名など)、2番目の引数には function を渡します。この function の中で実際のテストコードを書いていくことになります。

次にbeforeEach()で各exampleの実行前に処理したいコードを書きます。これも RSpec で言うところのbefore(:each)ブロックに対応してます。上のコードではオブジェクトの初期化を行っています。また、あらかじめ変数playersongbeforeEachの前に定義しておくことによってそれぞれのfunction中で使えるようにしていますね。

その後にit()でexampleの定義を行っています。これも引数を2つ取っていて、最初はexampleの説明、2番目のは実際のテストを配置した function となっています。やはり RSpec でのitブロックに対応しています。

it()の第2引数の function の中でexpect()を呼び出しています。expect(actual)という形式のあとに matcher メソッドをつなげる形です。actual というのはテストの実際の対象物というイメージです。Jasmine は actual の値と matcher メソッドの返り値を比較してテストの pass/fail を判断します。最近のバージョンの RSpec ではexpect(actual).to matcherという書き方が推奨されているので、この部分もまた対応していると言えます。上のコードで言いますと12行目でplayer.currentlyPlayingSongsongの値が等しいならばテストが pass となります。15行目で使われている custom matcher についてはここでは割愛します。

ここまでのまとめ

  • SpecRunner.htmlをブラウザで開くとテスト実行される。
    • Jasmine.js, プロダクトコード、テストコードを読み込み、Jasmine 設定&実行
  • プロダクトコードとテストコードは分離されている。
  • テストコードは RSpec の様に書ける。

とりあえずデモコードをそのまま動かした感じなので、次は自分で改造してみて動きをつかみたいと思います。

参考

TODO

  • custom matcher
  • 自分でテストコード書いてみる