JasmineによるJavaScriptのテスト その4

記事タイトルとURLをコピーする

こんにちは、プログラマのおぐらです。毎度おなじみ流浪のJasmineチュートリアルをお送りします。

前回のJasmineによるJavaScriptのテスト その3では、beforeEachafterEachによる「テストの事前準備と後始末」について解説しました。今回は実際にテストを記述する部分を詳細に説明していきます。

テストコード

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) abと同値であることを期待する
expect(a).toBe(b) abと同一オブジェクトであることを期待する
expext(a).toBeDefined() aが定義されていることを期待する(undefinedでない)
expect(a).toBeNull() anullであることを期待する
expect(a).toBeTruthy() atrueであることを期待する
expect(a).toBeFalsy() afalseであることを期待する
expect(a).toContain(b) abが含まれていることを期待する
expect(a).toBeLessThan(b) abより小さいことを期待する
expect(a).toBeGreaterThan(b) abより大きいことを期待する
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.jsspec/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.htmlbody要素内に<div id="test-node"></div>という記述を追加すればテストは成功しますが、そうするとすべてのテストがSpecRunner.htmlの内容に依存してしまいます。これを回避するには、beforeEachafterEachを使ってテスト時に動的にDOMツリーを操作する必要があります。

動的にDOMツリーを操作

DocumentSpec.jsを以下のように編集し、beforeEachメソッドとafterEachメソッドを追加します。

beforeEachでは、documentオブジェクトのcreateElementappendChildを利用してbody要素にテスト用のdiv要素を新しく追加しています。また、追加した要素をafterEachメソッドから参照できるよう、containerという変数に格納しています。

afterEachでは、後始末としてテスト用に追加したDOM要素をdocument.bodyから削除しています。

では、再度テストを実行してみましょう。

テスト結果

無事にテストが成功しました。

jasmine-domによるDOM構造の準備

jasmine-domを導入すると、先程のテストを以下のように簡潔に書くことができます。

だいぶスマートに記述できました。

Jasmineには、他にもjQuery用の拡張やiPhone用の拡張などがあり、Related Projectsにてアドオンの一覧が紹介されているので、参考にしてみてください。

今回はここまで。次回は最終回として「モック、スタブを使用したテストの書き方」について解説したいと思います。