こんにちは。今回は 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)
ブロックに対応してます。上のコードではオブジェクトの初期化を行っています。また、あらかじめ変数player
とsong
をbeforeEach
の前に定義しておくことによってそれぞれの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.currentlyPlayingSong
とsong
の値が等しいならばテストが pass となります。15行目で使われている custom matcher についてはここでは割愛します。
ここまでのまとめ
SpecRunner.html
をブラウザで開くとテスト実行される。- Jasmine.js, プロダクトコード、テストコードを読み込み、Jasmine 設定&実行
- プロダクトコードとテストコードは分離されている。
- テストコードは RSpec の様に書ける。
とりあえずデモコードをそのまま動かした感じなので、次は自分で改造してみて動きをつかみたいと思います。
参考
TODO
- custom matcher
- 自分でテストコード書いてみる