こんにちは、プログラマのおぐらです。毎度おなじみ流浪のJasmineチュートリアルをお送りします。
前回のJasmineによるJavaScriptのテスト その3では、beforeEachとafterEachによる「テストの事前準備と後始末」について解説しました。今回は実際にテストを記述する部分を詳細に説明していきます。
テストコード
Jasmineでは、テストコードがそのまま英文として読めるように設計されています(これはビヘイビア駆動開発の特徴でもあります)。
expectメソッドとMatcherメソッド
xUnit系のテスティングフレームワークにおけるassert系メソッドに相当するものが、expectメソッドとMatcherメソッドです。
テスト対象であるvalueが期待値である「5」と同一であることを検証する場合、xUnit系では、
assertEquals(5, value);
のように書きますが、Jasmineでは、
expect(value).toEqual(5);
のように書きます。
このように、Jasmineのテストコードは「expectメソッドが返すオブジェクトのtoXXXXXメソッドで、テスト対象が期待値どおりであるかどうかを評価する」という構造になっており、toXXXXXのような評価用のメソッドをMatcherと呼びます。
定義済みのMatcher
Jasmineでは以下のMatcherが定義されており、さらに自分で定義することもできます。
| Matcher | 意味 |
|---|---|
expect(a).toEqual(b)
|
aがbと同値であることを期待する
|
expect(a).toBe(b)
|
aがbと同一オブジェクトであることを期待する
|
expext(a).toBeDefined()
|
aが定義されていることを期待する(undefinedでない)
|
expect(a).toBeNull()
|
aがnullであることを期待する
|
expect(a).toBeTruthy()
|
aがtrueであることを期待する
|
expect(a).toBeFalsy()
|
aがfalseであることを期待する
|
expect(a).toContain(b)
|
aにbが含まれていることを期待する
|
expect(a).toBeLessThan(b)
|
aがbより小さいことを期待する
|
expect(a).toBeGreaterThan(b)
|
aがbより大きいことを期待する
|
expect(fn).toThrow(e)
|
fnが例外をスローすることを期待する
|
上記の一覧にはa != bのように否定形をテストするものがありませんが、Jasmineではexpect(a).toEqual(b)の否定形は、
expect(a).not.toEqual(b);
のように、expectメソッドとMatcherメソッドの間にnotをはさむことで表現します。
DOM操作を伴うテストの書き方
JasmineにはDOM操作を行う機能が提供されていません。そのため、テスト用のDOM要素は自分で用意する必要があります。
テストの準備
お馴染みのdocument.getElementByIdメソッドのテストを書いてみましょう。まず、新しくプロジェクトディレクトリにspec/DocumentSpec.jsを作成します。
このテストでは「getElementByIdでIDがtest-nodeのDOM要素を取得し、その結果がnullではないこと」をテストしています。
次に、このspec/DocumentSpec.jsを読み込むためにSpecRunner.htmlを以下のように変更します(第2回の「最初のテスト」で作成したSpecRunner.htmlの12行目にあるspec/ArraySpec.jsをspec/DocumentSpec.jsに変更しただけです)。
「SpecRunner.htmlを編集してテストファイルを読み込む」という作業はテストを追加した際に必ず必要となりますが、以降は記載を省略します。新しいテストファイルを追加した際は、適時SpecRunner.htmlを編集するようにしてください。
テストの実行
それではテストを実行してみましょう。ブラウザでSpecRunner.htmlを開いてみます。
テストは失敗となりました。
このテストでは、
var elementId = 'test-node';
expect(document.getElementById(elementId)).not.toBeNull();
という箇所で「DOMツリーにtest-nodeというIDを持ったDOMノードがあること」を期待しています。しかし、test-nodeというIDを持つノードは存在していないため、document.getElementById('test-node')がnullを返し、その結果テストが失敗したわけです。
SpecRunner.htmlのbody要素内に<div id="test-node"></div>という記述を追加すればテストは成功しますが、そうするとすべてのテストがSpecRunner.htmlの内容に依存してしまいます。これを回避するには、beforeEachとafterEachを使ってテスト時に動的にDOMツリーを操作する必要があります。
動的にDOMツリーを操作
DocumentSpec.jsを以下のように編集し、beforeEachメソッドとafterEachメソッドを追加します。
beforeEachでは、documentオブジェクトのcreateElementとappendChildを利用してbody要素にテスト用のdiv要素を新しく追加しています。また、追加した要素をafterEachメソッドから参照できるよう、containerという変数に格納しています。
afterEachでは、後始末としてテスト用に追加したDOM要素をdocument.bodyから削除しています。
では、再度テストを実行してみましょう。

無事にテストが成功しました。
jasmine-domによるDOM構造の準備
jasmine-domを導入すると、先程のテストを以下のように簡潔に書くことができます。
だいぶスマートに記述できました。
Jasmineには、他にもjQuery用の拡張やiPhone用の拡張などがあり、Related Projectsにてアドオンの一覧が紹介されているので、参考にしてみてください。
今回はここまで。次回は最終回として「モック、スタブを使用したテストの書き方」について解説したいと思います。