2010-12-07

Jasmineをもう少し詳しく紹介してみる

Jasmine: BDD for Javascript | Jasmine

先日プッシュした Jasmine についてもう少し掘り下げていきます。ただしテストの書き方については触れません。それは公式の情報やすでに詳しく紹介されている記事があります。

まとめ

  • Jasmine は見た目だけでなく考え方も RSpec の影響を受けているよ
  • 多少遠回りでも rubygems 版の Jasmine を使うとテストが本当に自動化できるよ

わざわざツールを使ってテストしたいということはある程度アプリケーションとして規模が大きいとか、サイトとして規模が大きく JavaScript の数が多くなっているので、人力チェックが大変だということではないでしょうか。

規模が大きくなってきた場合は、ある程度作法(ルール)を用意してそれを守ることが安全、安心なアプリケーションの開発、作成に欠かせません。Jasmine は単に JavaScript で JavaScript のテストコードが書けるだけではありません。より良い配置、より良い命名を促します。

特徴

見た目的にも明らかに RSpec の影響を受けていますが、構造的にもいわゆるヘルパー付きテスティングフレームワークの考え方を取り入れています。具体的に何が起きるかというと、

プロダクトコードもテストコードもいっぺんに読み込もうとします。

疑問点

上の特徴を踏まえると以下のような疑問が湧いてきます。

  • 1. HTML の中の JavaScript はどうやってテストするの?
  • 2. jQuery のチュートリアルなどによくある
$(document).ready( function() {
   ...
});

のようなコードはどうやってテストするの?

  • 3. 複数の JavaScript をいっぺんに読み込んだら他のコードに影響出たりしないの?

たぶんこれが回答

  • 1 については、テストしたいコードは HTML から分離しましょう
  • 2 と 3 はちゃんとオブジェクトに名前を付けてぶつからないようにしましょう

実はこれはもう

日本RubyKaigi2010 3日目 - まちゅダイアリー(2010-08-29)

を読めば想像がつきます。

ちょっとした効果にしか使わない場合は面倒に感じるかもしれませんが、コードをコードでテストする TDD や BDD は、やってないときに比べるとスタート時点では多かれ少なかれ面倒なものです。ここはそういうものと割り切ってください。あとでまた補足します。

使い方

二つあります。

  • standalone
  • rubygems + rake

standalone

以下に jasmine-standalone-1.0.1.zip の内容を挙げます。

├── SpecRunner.html
├── lib
│   └── jasmine-1.0.1
│       ├── MIT.LICENSE
│       ├── jasmine-html.js
│       ├── jasmine.css
│       └── jasmine.js
├── spec                <-- specファイル(テストコード)置き場
│   ├── PlayerSpec.js
│   └── SpecHelper.js
└── src                 <-- プロダクトコード置き場
    ├── Player.js
    └── Song.js

上の src/ にテストしたい JavaScript コードを、spec/ 以下に spec ファイル(テストコード)を置きます。spec ファイルの名前は

「元の名前 + [Ss]pec」.js

が推奨されています。実際には standalone の場合は自由ですが、gem 版を使うときにはこのルールがデフォルトなので、合わせておいた方がいいと思います。

そして各ファイルのパスを SpecRunner.html の中に書き、このファイルをブラウザで開くことですべてのテストが自動的に実行されます。

付属しているサンプル SpecRunner.html は以下のようになっています。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
  "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
  <title>Jasmine Test Runner</title>
  <link rel="stylesheet" type="text/css" href="lib/jasmine-1.0.1/jasmine.css">
  <script type="text/javascript" src="lib/jasmine-1.0.1/jasmine.js"></script>
  <script type="text/javascript" src="lib/jasmine-1.0.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>

</head>
<body>

<script type="text/javascript">
  jasmine.getEnv().addReporter(new jasmine.TrivialReporter());
  jasmine.getEnv().execute();
</script>

</body>
</html>

ここまでは見れば分かる通りです。QUnit と違ってサンプルの段階でテストを HTML の中に書くのではなく、テストを記述した spec ファイルとして分離することが推奨されています。

standalone 版はテストするコードが増えればこの HTML の編集の手間も増えていきます。

rubygems + rake

プロダクトコードとテストコードの配置については standalone 版と同じですが、SpecRunner.html の編集は必要ありません。ただし、当然

  • ruby
  • rubygems

が必要です。

面倒ですか? でもこれが用意できれば画期的に楽に安心を手に入れることができるようになります。もう少し続けましょう。

rubygems版Jasmine

インストール

gem install jasmine

で入ります。(Windows では試したことがないですが。)selenium など他にも依存しているライブラリがインストールされるので多少時間が掛かると思います。

いちばん基本的な使い方

Rails2, Rails3, その他用の使い方の説明が Using the Jasmine Gem | Jasmine に書かれていますが、ここでは最も何も考える必要のないその他用をもとに説明します。なお、Ruby と Rubygems はインストール済みとします。

すると jasmine のインストールから使い始めはこんな感じになります。

$ gem install jasmine
$ mkdir jasmine_test
$ cd jasmine_test
$ jasmine init
$ rake jasmine

これだけです。すると以下のように WEBRick サーバが立ち上がります。

(in /path/to/jasmine_test)
your tests are here:
  http://localhost:8888/
[2010-12-08 18:35:04] INFO  WEBrick 1.3.1
[2010-12-08 18:35:04] INFO  ruby 1.8.7 (2010-08-16) [i686-darwin9]
[2010-12-08 18:35:04] INFO  WEBrick::HTTPServer#start: pid=56941 port=8888

サンプルのSpecRunner.htmlを開いてJasmineを実行したところ

ここで http://localhost:8888/ にアクセスするとテストが実行されて右のように結果が返ってきます。

jasmine init したディレクトリの構成

このとき jasmine init を実行したディレクトリの中身は以下のようになっています。

├── Rakefile
├── public
│   └── javascripts
│       ├── Player.js
│       └── Song.js
└── spec
    └── javascripts
        ├── PlayerSpec.js
        ├── helpers
        │   └── SpecHelper.js
        └── support
            ├── jasmine.yml
            └── jasmine_runner.rb

あれ、SpecRunner.html がありません(わざとらしい)。実は ./spec/javascripts/support/jasmine.yml に秘密があります。コメントを省いて内容を掲載すると以下になります。

src_files:
    - public/javascripts/**/*.js
stylesheets:
helpers:
spec_files:
src_dir:
spec_dir:

ここで各種ファイルの位置と名前を指定します。基本的にデフォルトのままで大丈夫です。自動的に各種ファイルを拾い上げてそれを <script> で読み込むように作られた HTML がサーバから送られてくるので、テスト対象の JavaScript が増えても上のルールにマッチする限りは何もする必要がありません。

デフォルトと異なる場所に JavaScript を置きたい場合は jasmine.yml を書き換えてください。

jasmine gem の中身

├── MIT.LICENSE
├── README.markdown
├── bin
├── generators
├── jasmine
├── lib
│   ├── jasmine
│   │   ├── ...
│   │   ├── run.html.erb   <-- コレ
│   │   └── ...
│   └── jasmine.rb
└── spec

この中の run.html.erb がキモです。

http://localhost:8888/ にアクセスすると先ほどの YAML の中身が自動的にこの中に展開されたものがブラウザに返されます。

したがっていちいち SpecRunner.html を編集せずに、js ファイルを所定の場所に置くだけで自動的にすべて読み込むことが可能になるわけです。

rake jasmine:ci がすごい

gem 版の Jasmine では基本的には

rake jasmine

を実行して http://localhost:8888/ にアクセスしてテストするのですが、これを全自動にするのが

rake jasmine:ci

です。これは 1selenium を利用してブラウザ(デフォルトは Firefox)を操作してテストの結果を得ます。つまり上のコマンドを叩いたあとはただ待つだけです。

(in /path/to/jasmine_test)
/opt/local/bin/ruby -S rspec --color --format progress "spec/javascripts/support/jasmine_runner.rb"
Waiting for jasmine server on 56662...
[2010-12-08 18:41:30] INFO  WEBrick 1.3.1
[2010-12-08 18:41:30] INFO  ruby 1.8.7 (2010-08-16) [i686-darwin9]
[2010-12-08 18:41:30] INFO  WEBrick::HTTPServer#start: pid=57246 port=56662
Waiting for jasmine server on 56662...
jasmine server started.
Running: java -jar "/opt/local/lib/ruby/gems/1.8/gems/selenium-rc-2.2.4/vendor/selenium-server.jar" -port 56670 > /dev/null
==> Waiting for Selenium RC server on port 56670... Ready!
Waiting for suite to finish in browser ...
.....

Finished in 0.18192 seconds
5 examples, 0 failures

こんな結果が返ってきます。(MacPorts で入れた Ruby で実行。)

ブラウザがすでに起きていても新たに起動するところから始めるので動作は遅いのですが、全自動で実行できるのは魅力です。これを CI サーバで実行するようにしておけばいつも JavaScript のコードが正しく動いているかテスト済みの状態を維持できるようになるわけです。

こうなるととても安心です。各種ライブラリのバージョンを上げたときの検証など、大幅に省力化できるのではないでしょうか。

参考

  1. 今のところ 

About

例によって個人のなんちゃらです