書籍転載:JavaScriptライブラリ実践活用[厳選111]
RSpec風の構文でBDD用のテスト・コードを記述する[Jasmine]
書籍転載の11本目(書籍内の番号は「108」)。RSpec風のテスト・コードが書けるBDD(ビヘイビア駆動開発)テスティング・フレームワーク「Jasmine」の基本的な使い方を説明する。
書籍転載について
ご注意
本記事は、書籍の内容を改変することなく、そのまま転載したものです。このため用字用語の統一ルールなどはBuild Insiderのそれとは一致しません。あらかじめご了承ください。
Jasmineは(Rubyでデファクトスタンダードになっている)RSpec風のテストコードを書くための、BDDテスティングフレームワークです。期待する振る舞いをまず定義し、その振る舞いを検証するテストコードを書き、テストを通すように製品コードを書いていきます。
Jasmineを使うと、期待する振る舞いに対して、付随する初期処理、関連するテストを構造化して記述することができるため、テストコードの可読性が上がります。また、検証処理にFluent(流れるような)インタフェースによる検証コードが書けることも特徴です。
- 名称: Jasmine
- 分類: テスト
- URL: http://pivotal.github.com/jasmine/
- 関連ファイル: jasmin.js、jasmin-html.js、jasmin.css
テストコード
Jasmineを利用するには、まずテストコードを用意する必要があります。リスト108-01がFizzBuzz*1のテストコードサンプルです。Jasmineではテストコードを独立したファイルとしてテスト対象コードと対になるように作成し、「(テスト対象コードファイル名)Spec.js」という名前で作成するのが一般的です。
- *1) 1から順に数え、「3」で割り切れる数の時は「Fizz」、「5」で割り切れる数の時は「Buzz」、「3」と「5」の両方で割り切れる数の時は「FizzBuzz」という遊び
/// <reference path="jasmine.js"/>
/// <reference path="jasmine-html.js"/>
/// <reference path="fizzBuzz.js"/>
// 1テストストーリーの定義
describe("FizzBuzzの正常テスト", function () {
var fizzBuzz;
// 4それぞれのテストケースが実行される前に行う処理を記載
beforeEach(function () {
fizzBuzz = new FizzBuzz();
});
// 2テストケースの定義
it("数字を返す", function () {
// 3値の検証
expect(fizzBuzz.speak(1)).toEqual("1");
expect(fizzBuzz.speak(2)).toEqual("2");
});
it("3の倍数でFizzを返す", function () {
expect(fizzBuzz.speak(3)).toEqual("Fizz");
expect(fizzBuzz.speak(6)).toEqual("Fizz");
});
it("5の倍数でBuzzを返す", function () {
expect(fizzBuzz.speak(5)).toEqual("Buzz");
expect(fizzBuzz.speak(10)).toEqual("Buzz");
});
it("3の倍数かつ5の倍数でFizzBuzzを返す", function () {
expect(fizzBuzz.speak(15)).toEqual("FizzBuzz");
expect(fizzBuzz.speak(30)).toEqual("FizzBuzz");
});
});
describe("FizzBuzzの異常テスト", function () {
var fizzBuzz;
beforeEach(function () {
fizzBuzz = new FizzBuzz();
});
it("引数指定なしで例外発生", function () {
// 例外の検証
expect(function () { fizzBuzz.speak(null); })
.toThrow(new ArgumentNullException());
});
});
|
テストコードはユースケースごとにテストストーリーをdescribeメソッドで定義し(.)、テストストーリーに沿った個々のテストケースをitメソッドで定義する(.)、というように構造化して記載します。describeメソッドは入れ子構造にすることも可能です。
各テストケースでは、expectメソッド、Matcherと呼ばれるアサーションメソッドで値の検証を行います(.)。使用できるMatcherおよびその構文を表108-01に示します。
Matcher | 構文 | 説明 |
---|---|---|
toBe | expect(検証オブジェクト).toBe(期待オブジェクト) | 期待オブジェクトと同じオブジェクトかどうか検証する |
toEqual | expect(検証値).toEqual(期待値) | 期待値と同じ値かどうか検証する |
toMatch | expect(検証値).toMatch(正規表現) | 正規表現に合致するかどうか検証する |
toBeDefined | expect(検証値).toBeDefined() | 定義済みかどうか検証する |
toBeNull | expect(検証値).toBeNull() | nullかどうか検証する |
toBeTruthy | expect(検証値).toBeTruthy() | 真とみなせる値かどうか検証する |
toBeFalsy | expect(検証値).toBeFalsy() | 偽とみなせる値かどうか検証する |
toContain | expect(検証配列).toContain(含まれることを期待する値) | 期待した値が配列に含まれているかどうか検証する |
toBeLessThan | expect(検証値).toBeLessThan(比較値) | 比較値より小さい値かどうか検証する |
toBeGreaterThan | expect(検証値).toBeGreaterThan(比較値) | 比較値より大きい値かどうか検証する |
toBeCloseTo | expect(検証値).toBeCloseTo(比較値, 精度) | 指定した精度に丸めた値が比較値と同じかどうか検証する |
toThrow | expect(検証する処理を行う関数).toThrow(期待する例外オブジェクト) | 期待する例外が発生するかどうか検証する |
not | expect(検証値).not.toBe(期待値) | 後続のMatcherの条件を満たさないことを検証する |
1つのテストストーリー内で共通する初期化処理が必要なら、beforeEachメソッドで定義します(.)。サンプルにはありませんが、共通の終了処理はafterEachメソッドで定義します。
テスト対象コード
テストコードができたところで、次はテスト対象コードを作成します。Jasmineでは独立したファイルとして作成することが望ましいです。テスト対象コードはリスト108-02のとおりです。
// 引数なし例外
var ArgumentNullException = function () { };
// FizzBuzz用クラス定義
var FizzBuzz = (function () {
// FizzBuzzクラス作成
var FizzBuzz = function () { };
// 指定した値に対応するFizzBuzz結果を返すメソッド
FizzBuzz.prototype.speak = function (n) {
// 引数が設定されていなければエラー
if (!n) throw new ArgumentNullException();
// 3で割り切れるときはFizz
if (n % 3 === 0) return "Fizz";
// 5で割り切れるときはBuzz
if (n === 5) return "Buzz";
// 3でも5でも割り切れるときはFizzBuzz
if (n % 3 === 0 && n % 5 === 0) return "FizzBuzz";
// 上記いずれでもないときは引数のまま
return n.toString();
};
// 定義したクラスを返す
return FizzBuzz;
})();
|
テスト実行用Webページ
公式サイトからStandalone版をダウンロードし、その中に含まれる「SpecRunner.html」をコピーして作成します。その際、テストコードとテスト対象コードのインポート部分をリスト108-03のように修正します。
<!--テスト対象コードをインポート -->
<script type="text/javascript" src="js/fizzbuzz.js"></script>
<!--テストコードをインポート -->
<script type="text/javascript" src="js/fizzbuzzSpec.js"></script>
|
テスト実行結果
それではテストを実行してみましょう。リスト108-03で作成したjasmine.htmlをWebブラウザで開くと、図108-01のようにテスト結果が表示されます。各部について説明しましょう。
1テストの実行時間
すべてのテストを実行するのにかかった時間が表示されます。
2テストケースの実行結果マーク
すべてのテストケースの実行結果をマークで表示します。失敗したテストケースがあると、"×"が表示されます。
3テスト結果の概要
すべて成功すれば成功したテストケースの数が、一つでも失敗すれば失敗したテストケースの数が概要として表示されます。
4全テストケース数
すべてのテストケース数が表示されます。この部分はリンクになっており、クリックすると図108-02の全テストケース一覧画面が表示されます。
全テストケース一覧画面では、図108-02の枠で囲った部分のようにリスト108-01で記載したテストストーリー、テストケースが階層化されて表示されます。それぞれテストが成功していれば緑、失敗していれば赤で表示されます。
テストストーリー、テストケースそれぞれはリンクになっていて、クリックすることで指定したテストストーリー、テストケースだけのテストを再実行できます。
図108-03はテストストーリー「FizzBuzzの正常テスト」をクリックした画面です。この画面の枠で囲った部分には部分実行したテストケース数の概要が表示されます。この部分もリンクになっていて、クリックすることで再度全部のテストケースが実行されます。
5失敗テストケース数
失敗したテストケース数が表示されます。この部分はリンクになっており、クリックすると失敗したテストケースだけ表示します。
6失敗テストケース
失敗したテストケースが表示されます。ここにはリスト108-01でテストケースを定義する際、describeメソッドに指定したテストストーリーと、itメソッドに指定したテストケースのテキストが表示されます。
7失敗理由
テストが失敗した理由が表示されます。検証に利用したMatcherによりメッセージが異なりますが、どのような原因で失敗したのかどうか確認することができます。
「Expected '10' to equal 'Buzz'.」という結果の内、'10'はexpectメソッドに渡した値、'Buzz'はtoEqualメソッドに渡した値です。この例では'Buzz'を期待したが'10'になってしまっているということになります。
8例外表示設定
テスト中に例外が発生した場合、既定ではテスト結果として例外が発生したことが表示されます。しかし、例外の表示をブラウザに任せたい場合もあるでしょう。そんなときはここにチェックを入れることで、Jasmineによる例外処理が行われなくなります。
※以下では、本稿の前後を合わせて5回分(第9回~第13回)のみ表示しています。
連載の全タイトルを参照するには、[この記事の連載 INDEX]を参照してください。
9. [Underscore.js]さなざまなコレクション操作を行う
書籍転載の9本目(書籍内の番号は「100」)。ユーティリティ・ライブラリ「Underscore.js」の基礎として、さまざまなコレクション操作する方法を説明。
10. [Underscore.js]テンプレートとオブジェクトから文字列を生成する
書籍転載の10本目(書籍内の番号は「101」)。テンプレートとオブジェクトをバインドし、その結果を出力できる「Underscore.js」のテンプレートAPIの使い方を説明。
11. 【現在、表示中】≫ RSpec風の構文でBDD用のテスト・コードを記述する[Jasmine]
書籍転載の11本目(書籍内の番号は「108」)。RSpec風のテスト・コードが書けるBDD(ビヘイビア駆動開発)テスティング・フレームワーク「Jasmine」の基本的な使い方を説明する。
12. JasmineのSpy機能でテストダブルを作成する
書籍転載の12本目(書籍内の番号は「109」)。Jasmineでテスト対象オブジェクトが持つメソッドの戻り値を固定値に変更したり、そのメソッドが実行されたかどうかを検証したりするために、Spy機能を使用する方法を解説。
13. 短くかつ安全で高性能なコードを書く[CoffeeScript]
書籍転載の13本目(書籍内の番号は「62」)。短い記述で、安全かつ高性能なJavaScriptコードを生成できる「CoffeeScript」の基本的な使い方を説明する。