技術ブログ - 毎日が成長!

‘JavaScript’ タグのついている投稿

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

2010年12月24日 by ogura

こんにちは、プログラマのおぐらです。毎度おなじみ流浪の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にてアドオンの一覧が紹介されているので、参考にしてみてください。

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

 

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

2010年12月7日 by ogura

こんにちは、プログラマのおぐらです。

前回のJasmineによるJavaScriptのテスト その2から1週間ほど空いてしまいましたが、今回もJasmineのチュートリアルをお送りします。

テストの事前準備と後始末

他のテスティングフレームワークと同様に、Jasmineにもテスト毎に事前準備と後始末を行う仕組みがあります。Jasmineでは、テスト準備のためにbeforeEachメソッド、後始末のためにafterEachメソッドが提供されています。

Jasmineのテストは、describeメソッドに渡す無名関数内にbeforeEachitメソッド呼び出しを記述し、さらにそれらに渡す無名関数の中で実際のテストコードを記述するというスタイルになっています。が、厳密な記述をしていくとだいぶ文章が読みづらくなってしまうため、以降の説明ではRSpecなどのフレームワークと同じように「describeメソッドの中」、「itメソッド内」と表記します。厳密さが気になる方は適宜読み替えてください。よろしくお願いします。

beforEachメソッド

describe内に書かれた各itメソッド呼び出しの前に毎回実行されるメソッドがbeforEachメソッドです。前回、「最初のテスト」として以下のテストを作成しました。

これにbeforeEachメソッドを追加したコードを以下に示します。

最初のテストで使用していたarr変数をbeforeEachメソッド内で初期化するようになりました。こうする事によって、arr変数に対して複数のテスト(=itメソッド)を追加していった場合でも、共通する初期化処理を1箇所にまとめることができます。

では、具体例を見てみましょう。

ここでは、Arrayクラスのshiftメソッドに対するテストを追加しました。追加したテストの内容は、

  • shiftメソッドの返り値が配列の先頭要素の1である
  • shiftメソッド実行後のArrayオブジェクトの先頭要素が2に変わっている

というものです。

では実行してみましょう。

テスト実行結果

追加したテストも意図どおりに動作しているようです。

今回の例では、それぞれのitメソッド間で共有しているarr変数の内容を、新たに追加した「shiftメソッドにより配列の先頭要素を取り出す事ができる」で変更していますが、既存の「lengthプロパティで配列長を取得する事ができる」の結果には影響していません。これは、itメソッドが実行される前にbeforeEachメソッドが毎回実行され、beforeEachメソッド内のarr = [1, 2, 3];という箇所で、毎回arr変数が初期化されているためです。

afterEachメソッド

beforeEachメソッドとは逆に、describe内の各itメソッドが実行された直後に毎回実行されるのがafterEachメソッドです。たいていの場合はbeforeEachメソッドによる初期化処理のみで事足りますが、明確に後始末を行う必要がある場合にはこのメソッドを利用します。

例えばDOM操作を伴うテストの場合、beforeEachメソッドで「document.bodyにテスト用のDOM要素を追加する」という処理を行い、afterEachメソッドでは「追加したDOM要素を削除する」という処理を書いておけば、テスト同士が影響しないようにすることができます。

次回に続きます。

 

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

2010年12月1日 by ogura

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

前回のJasmineによるJavaScriptのテスト その1に引き続き、今回もJasmineのチュートリアルになります。今回は、JavaScriptの組み込みクラスであるArrayを対象に、簡単なテストの作成と実行を行います。

基本的なテストの書き方

前回、Jasmineのインストール方法として

  • JavaScript単独のプロジェクトで使用する場合(jasmine-standalone-X.X.X.zipを展開)
  • Rubyプロジェクトとともに使用する場合(gem install jasmineを実行)

の2通りの方法を紹介しましたが、テストの書き方は基本的にいずれの場合も同じで実行方法が異なっています。このチュートリアルでは、より汎用的な「JavaScript単独のプロジェクトで使用する場合」を例に説明していきます(Rubyプロジェクトとともに使用する場合については機会を改めて解説する予定です)。

最初のテスト

最初のサンプルとして、JavaScriptの組み込みクラスArrayのテストを以下に示します。

上記のように、テストは、

  • describeメソッドにクラス等のテスト対象を記述し…
  • describeメソッド内に入れ子になったitメソッドにテスト対象の振る舞いを記述し…
  • itメソッド内に入れ子になったexpectメソッドの引数にテスト対象を記述し…
  • toXXXXXメソッドの引数に期待値を記述する

という構造をしています。

ちなみに、上記のサンプルは、

  • テスト対象となる配列arrを作成し…
  • arr.length3であることを確認する

という内容です(expectメソッドの書き方については次回解説します)。

では実際に上記サンプルの動作を確認してみましょう。

テストを実行するには、まず上記のテストコードをプロジェクトディレクトリのspec/ArraySpec.jsとして保存します。

次に、保存したArraySpec.jsを実行するため、プロジェクトディレクトリ直下のSpecRunner.htmlを以下のような内容にします。

あとはSpecRunner.htmlをブラウザで開くだけです。

文字化けしたテスト結果

ん、文字化けしてますね。デフォルトのSpecRunner.htmlのままでは、日本語を使用した場合にブラウザによっては文字化けすることがあります。

Web開発をされている方ならもちろんお分かりだと思いますが、これはJasmineの問題ではなく、よくあるHTMLの文字化けです。SpecRunner.htmlのHEAD要素内に以下の1行を記述すれば文字化けは直ります(ファイルの文字コードエンコーディングがUTF-8の場合)。

<meta http-equiv="content-type" content="text/html; charset=UTF-8">

ブラウザをリロードすると、以下のようにテストの実行結果が正しく表示されます。

テスト結果

このテスト結果をよく見ると、

Arrayクラスはlengthプロパティで配列長を取得する事ができる」という仕様が成功(グリーン)

と読めるのではないでしょうか?

このことからも、Jasmineのテストは「自然言語で記述された要求仕様書のように見える」というビヘイビア駆動開発(BDD)の特徴を持っていることがわかるかと思います。

次回は、xUnit系のテスティングフレームワークとの対比を中心に説明していきます。