Jasmineでのユニットテスト

本稿は、Sencha公式ドキュメントの Unit Testing with Jasmine の翻訳です。

I. 導入

このチュートリアルでは既存のExtアプリケーションを取り上げ、ユニットテストするJasmineアサーションライブラリを紹介します。 読者はJavaScript, Ext JS 4, MVCアーキテクチャ及びHTML, CSSの基本及びリソースの使い方について精通している人が対象です。

なぜテストするのか? アプリケーションをテストするには多くの理由があります。 テストはすべてのユースケースを手動で消し込んでゆくことなくアプリケーションの機能性を検証できます。 さらに、アプリケーションがリファクタリングやアップデートされる時に、 テストはそれらの変更がシステムに当たらしバグを呼び込まないように検証することが出来ます。

II. はじめに

このチュートリアルではExtJSにバンドルされた ExJSのMVCにバンドルされている”simple”というアプリケーションを使います。 このファイルは (ext)/examples/app/siple にあります。このソースをワークスペースなどにコピーしてください。

次のフォルダを追加してください

[text] <simple dir>/app-test <simple dir>/app-test/specs [/text]

Jasmineのスタンドアロンライブラリをダウンロードしてapp-testフォルダーに展開します。

次のファイルを作成します(現在は空のままでいいです、次の段階で中身を書きます)

[text] <simple dir>/app-test.js <simple dir>/run-tests.html [/text]

: これらのファイル名は任意です。 どんな名前をつけてもらっても結構です。 名前はそれが何かを簡単に示しています。 app-test.jsファイルは本質的にapp.jsのテスト用で、run-tests.htmlは単なるブートストラップです。

プロジェクトは現在、次のような状態でしょう。

fig

ファイルとフォルダーの設定が完了したので、テストの実行環境を作ります。 それはWebページで、表示したときにテストが実行され結果が報告されます。 runt-test.htmlを開いて次のマークアップを追加します。

[html] <html> <head> <title id="page-title">Tester</title> <link rel="stylesheet" type="text/css" href="app-test/lib/jasmine-1.1.0/jasmine.css"> <script type="text/javascript" src="extjs/ext-debug.js"></script> <script type="text/javascript" src="app-test/lib/jasmine-1.1.0/jasmine.js"></script> <script type="text/javascript" src="app-test/lib/jasmine-1.1.0/jasmine-html.js"></script> <!– include specs here –> <!– test launcher –> <script type="text/javascript" src="app-test.js"></script> </head> <body> </body> </html> [/html]

いくつかのキーとなる事があります覚えてください。 Jasmineリソース、Ext JS フレームワークリソース、app-test.jsです。 これらはテストにインクルードされる必要があります(この順番が重要です)。 スペック(Jusmineアサーションjsファイル)をapp-testの前に、その後に残りのファイルをインクルードします。

次にapp-test.jsを開き次のコードをその中に書きます。

[js] Ext.require(‘Ext.app.Application’); var Application = null; Ext.onReady(function() { Application = Ext.create(‘Ext.app.Application’, { name: ‘AM’, controllers: [ ‘Users’ ], launch: function() { //include the tests in the test.html head jasmine.getEnv().addReporter(new jasmine.TrivialReporter()); jasmine.getEnv().execute(); } }); }); [/js]

上記のコードでは、Applicationインスタンスへのグローバルリファレンスを作成しJasmineアサーションライブラリーを起動します。 Applicationオブジェクトを直接作成して参照を保存することによって完了し、ドキュメントが準備されたときに Ext.application() メソッドをバイパスします。

: このApplication定義はapp.jsの通常のApplication定義からのコピペではありません。 このバージョンはコントローラー、ストア、モデルなどをインクルードするだけです。 そしてlaunchメソッドがコールされるとJamineテストが実行されます。

動作するテスト環境が整いました。 確認のためrun-tests.htmlファイルをブラウザで開いてみましょう。 グリーンのバーに、reads 0 specs, 0 failures in 0s と書かれたJasminのUIが表示されます。

fig

III. テストを書く

specsフォルダ(/app-test/specs)の下に次の名前で二つの空のテキストファイルを作ります。

[text] example.js users.js [/text]

次にrun-tests.htmlファイルに戻り次の2行を <!– include specs here –> コメントの下に追加します。

[html] <!– include specs here –> <script type="text/javascript" src="app-test/specs/example.js"></script> <script type="text/javascript" src="app-test/specs/users.js"></script> [/html]

: .spec.jsのように二重の拡張子を使う使用例がありますが、それは必須ではありません。 この方法はファイルがなんのためのファイルかよく表していますが、 このケースでは.spec.jsといった二重の拡張子のではなくspecsフォルダーを使っています。

specs/example.jsの中を書くことから始めましょう。 Jasmineの仕様書文法はとても説明的です。 それぞれのテストスイートにはdescripe関数があり、それぞれのテストは”it”関数によって定義されます。

使用例:

[js] describe("Basic Assumptions", function() { it("has ExtJS4 loaded", function() { expect(Ext).toBeDefined(); expect(Ext.getVersion()).toBeTruthy(); expect(Ext.getVersion().major).toEqual(4); }); it("has loaded AM code",function(){ expect(AM).toBeDefined(); }); }); [/js]

テストするには(それぞれの”it”ブロックで) expect(someValue).toBe<something>() をコールするだけです。

次はもう少し複雑な例です。 非同期なストアをテストしコントローラーから結果を取り出します (これはグローバルアプリケーションリファレンスが便利に使えるところです)。 このコードはspecs/users.jsに記述します。

[js] describe("Users", function() { var store = null, ctlr = null; beforeEach(function(){ if (!ctlr) { ctlr = Application.getController(‘Users’); } if (!store) { store = ctlr.getStore(‘Users’); } expect(store).toBeTruthy(); waitsFor( function(){ return !store.isLoading(); }, "load never completed", 4000 ); }); it("should have users",function(){ expect(store.getCount()).toBeGreaterThan(1); }); it("should open the editor window", function(){ var grid = Ext.ComponentQuery.query(‘userlist’)[0]; ctlr.editUser(grid,store.getAt(0)); var edit = Ext.ComponentQuery.query(‘useredit’)[0]; expect(edit).toBeTruthy(); if(edit)edit.destroy(); }); }); [/js]

“beforeEach”関数に(この関数はそれぞれの”it”の前にコールされます)注目してください。 この関数はそれぞれのテストのステージをセットアップします、この例では:

  1. コントローラーからストアを取得します。
  2. ストアを取り出すのに成功したこと(nullまたはundefinedではない)を確認します。
  3. ストアのロードが完了するのを待ちます。”waitFor”関数をご覧ください。このストアは生成されると自動的にデータをロードします。これが準備される前にはテストは実行されません。

IV. 自動化

これをPhantomJSと結合するとテストをコマンドラインやcronジョブから実行できるようになります。 PhantomJSディストリビューションで提供されるrun-jasmin.jsは必要なものの全てです。 出力を必要に応じて変更することが出来ます。 ここ に変更した例があります。

コマンドラインの例:

[bash] phantomjs run-jasmine.js http://localhost/app/run-tests.html [/bash]

XHRはfile://プロトコルからは作られないので、テストはWebサーバーからする必要があります、

PhantomJSのセットアップ

WindowsやMac(Mac portを使わない場合)では ここ. からバイナリをダウンロードする必要があります。 ダウンロードしたらどこか使いやすい場所に解凍します。 (例: /Applications/PhantomeJS.) 次にPATH環境変数にPhantomJSの実行ファイルを含めるように変更します。 Macの場合ですとターミナルを開いて、.bashrcまたは.profileを編集し次の行を最後に追加します。

[bash] export PATH=$PATH:~/Applications/PhantomJS/bin [/bash]

Windowsの場合ですと、システムのプロパティを開いて、詳細設定タブを選択し、環境変数ボタンをクリックします。 ユーザーの環境変数のPATHを探して編集しその終わりに次のものを追加します。

[bash] ;%USERPROFILE%\Applications\PhantomJS [/bash]

ユーザーの環境変数にPATHのエントリーがない場合はそれを追加して次の値を設定します。

[bash] %PATH%;%USERPROFILE%\Applications\PhantomJS [/bash]

OKボタンをクリックして編集結果を保存しウィンドウを閉じます。

PhantomJSのセットアップ (別な方法)

LinuxユーザーやMac portsユーザーの場合は、ターミナルからPhantomJSをインストールする簡単なコマンドがあります。

  • Debian(Ubuntu) ベースのディストリビューション

    [bash] sudo apt-get install phantomjs. [/bash]
  • RedHat(Fedora) ベースのディストリビューション

[bash] su -c ‘yum install phantomjs’ [/bash]
  • Mac ports では
[bash] sudo port install phantomjs. [/bash]

PhantomJSの確認

確認するには、ターミナル(またはコマンドプロンプト)ウィンドウを開いて次のようにタイプします。

[bash] phantomjs –version [/bash]

なんらかの出力(“command not found”以外の)が得られたらPhantomJSは使用可能です。

作者について: Jonathan Grimes (facebook, twitter, google+) は、現在オンライン教育の統合プラットフォームを構築している新興企業である NextThought のソフトウェアエンジニアです。