テストのオーサリング

wikiholmes edited this page Jan 12, 2011 · 7 revisions

Windmill 日本語ドキュメント

Windmill IDE はテストの作成、編集、出力のモニタリングやブラウザ上での Javascript を使った通信を実現するグラフィカルなツールです。

IDE の起動

windmill firefox http://www.example.com

(Mac 上で Safari を使う場合は上記 firefox の部分を safari に変更してください)

上記コマンドの実行により Firefox が起動、Windmill が読み込まれます。BR

Windmill の読み込みが完了すると2つのウィンドウが表示されます。ひとつはテスト対象アプリケーションのページ(http://www.example.com) で、もうひとつが Windmill IDE です。

BR
h2. テストのレコーディング

レコーディング機能を使うことで、迅速なテスト作成が可能になります。

  • まず対象サイトのテストを開始したいページに移動します。
  • IDE のレコード・ボタン(ウィンドウ右上に並んだ丸いボタンの一番左側)を押します。するとボタンのアイコンが丸から四角に変わり、ブラウザのフォーカスが IDE からメインウィンドウに移ります。
  • テスト操作(つまりそのページでの普通のナビゲーション操作)を実行します。
  • テスト操作が完了したら、もう一度レコード・ボタンを押します。ボタンのアイコンは四角から丸へ戻ります。
  • “Waits”: Controller API: テストの過程で、新たなページへの移動や Ajax リクエストの応答受信を確認してから次のステップの実行に移るには、waits のステップが必要となります。
  • “Open”: Controller API: ブラウザのアドレスバーに直接 URL を入力してもその内容は記録されません。特定の URL へ移動させたいきは open メソッドを使用します。
  • Assertions: 記録した操作の結果が期待したものかどうかをテストするのが asserts メソッドです。

テストの編集

  • IDE の編集画面はテスト対象ページで実行するアクションがステップ毎に一覧表示されます(この編集画面を使わずに直接ファイルを編集することも可能です)。
  • ドロップダウンリストで特定のメソッドを選択すると、そのメソッドに対応するオプションのフィールドが入力可能になります。
  • 上向きや下向きの矢印アイコンを操作することで、あるステップを別のステップの前に実行させたり後にしたり順序を変更できます。
  • ごみ箱アイコンをクリックするとそのステップは削除されます。ただし削除の際、確認は求められないので要注意!

テストの再生

  • IDE ウィンドウ上部の play アイコンをクリックすると IDE のステップをすべて最初から実行します。
  • 各ステップに毎に表示されている再生アイコンをクリックすると、そのステップから開始してカスケードされたテストがなくなるまで実行します(この挙動は settings で変更可能です)。
  • ステップの実行結果が成功か失敗かより、表示色は緑または赤に変わります。

テストの保存

save アイコンをクリックするとテスト内容は Python コードに変換され別ウィンドウで表示されます(IDE の settings パネルで JavaScript など他の形式で出力するように変更することも可能です)。出力されたコードを後で再実行するためには、ブラウザの「名前を付けて保存」機能を使ってディスクに保存してください。

DOM エクスプローラー

DOM エクスプローラーはアプリケーションの DOM 階層を探索、(DOM ノードのクリックにより)選択されたノードがアクションのターゲットとなるように IDE を更新してくれます。

  • IDE ウィンドウ右上にある X アイコンのボタン(右から2番目)を押すと DOM エクスプローラーがスタートします。
  • メインウィンドウにフォーカスが移ったら、ページ上でマウスを動かしてみましょう。マウスポインタの位置の DOM 要素が枠で囲まれて表示されます。
  • IDE にアクションが既に定義されている場合、ページ上でマウスの動きに合わせてアクションのフィールド内容が変化します。マウスをクリックしてノードを確定するとエクスプローラーが停止、IDE ウィンドウにフォーカスが戻って再び編集可能状態になります。

アサーションツール

アサーションツールを使うことで迅速なアサーション定義が可能になります。この機能は DOM エクスプローラーによく似ていますが、使用する目的はまったく異なります。

  • ウィンドウ右上、一番右側にある A アイコンのボタンをクリックするとアサーションツールが有効になります。
  • メインウィンドウにフォーカスが移り、その上でマウスを動かすとマウスポインタが位置するノードの情報が IDE 側に表示されます。
  • アサーションに使用するノードが見つかったらその上でマウスをクリックします。
  • フォーカスが IDE 側に戻ると、新たなアクションが自動的に追加されています。このアクションにはクリックしたノードの状態が記録され、再度テストを実行したときノードの状態が同じかを確認するようになります。

Firebug Lite の統合

Windmill は Firebug Lite を内部に組み込んでおり、どのブラウザを使っている場合でも Firebug と同等の有用な機能を利用できるようになっています。

Output と Performance タブ

Windmill IDE を使ったテストにおいて、出力はすべて Output タブに送られるため何か起こったのかをすべて把握できるようになっています。また処理パフォーマンス情報は Performance タブに記録され、アプリケーションに新機能の追加や拡張を施したときに必要なパフォーマンスの確認ができるようになっています。

Settings ダイアログ

Settings ダイアログにはいくつかの設定可能項目が表示され、これらを変更することでテスト実行環境をより快適なものにすることができます。

System

Throw JavaScript errors, full debug:: デフォルトは無効で、JavaScript のエラーは Output タブに出力されるようになっています。これを有効にすること、Firebug やブラウザのコンソールなどでエラーを確認できるようになります。 Break on Failure:: この項目を有効にすると、テスト実行中にエラーを検知するとすぐに停止するようになります。無効になっているときは成功、失敗にかかわらずすべてのテストを実行し続けます。 Suppress Javascript Alerts:: 有効にすると、すべての JavaScript Alert をテスト実行ウィンドウで補足するようになります。BR 詳細: http://trac.getwindmill.com/wiki/ControllerApi Suite Save Formatting:: save をクリックしたときに変換出力されるコードの形式を指定することができます。

IDE/Recorder

Auto Scroll IDE to bottom:: テスト実行中の IDE 自動的スクロール機能を有効にすると、最新のアクションが常に表示されるようにできます。 Absolute click sensitivity when recording:: テストのレコーディングで様々な操作をおこなっていると、ダブルクリック操作が2つのシングルクリックなどとして記録されてしまうことがあります。このような誤認識を避け、厳密に記録したい場合はこの機能を無効にします。ただしこれを無効にすることで逆にシングルクリックが認識されにくくなる場合もありますので、環境に合わせて設定を変更してください。 Action play button cascades:: 複数のテストステップを作成するときこの項目を有効にしておくと、各ステップはひとつの連続した(カスケード)ステップとして記録され、ステップの再生ボタンを押すとカスケードステップがすべて実行されるようになります。無効にして作成したステップは単独で再生されます。 Use XPath only for explorer/recorder:: テスト対象アプリケーションで複数の DOM 要素が同じ ID を使用していたり、動的な ID が使われているため事前に ID を特定ができない場合、この機能を有効にして必ず XPath 形式で記録するようにできます。この場合要素が ID を持っていても、設定を無効にしない限り ID 形式で記録されることはありません。 Record only absolute XPath’s (vs relative):: この機能を有効にすると XPath を div 要素の ID 等を含む絶対パスで記録します。この方式は多少確実性が上がり、説明的な記述となりますが、すべてのブラウザで 100% の互換性が確保できているとは言えません。 Play action by hitting ‘return/enter’:: この機能が有効になっている場合、ステップのテキスト入力フィールドでリターンキーを押すと、再生ボタンを押さなくても即座にステップの再生を実行します。

Python テスト

Windmill は functest モジュールを使ってテスト実行が可能なコントローラー実装を提供しています。

# Generated by the windmill services transformer
from windmill.authoring import [[WindmillTestClient]]

def test():
    client = [[WindmillTestClient]](__name__)

    client.click(id=u'view[[NavCenterRight]]')
    client.waits.sleep(milliseconds=2000)
    client.doubleClick(id=u'hourDiv1-1200')
    client.waits.sleep(milliseconds=2000)
    client.type(text=u'Properties Test', id=u'noteTitle')
    client.type(text=u'9:00', id=u'startTime')
    client.type(text=u'4:00', id=u'endTime')
    client.radio(id=u'startMeridianAM')
    client.radio(id=u'endMeridianPM')
    client.select(id=u'eventStatus', option=u'Tentative')
    client.type(text=u'A description for the properties test', id=u'noteDescription')

WindmillTestClient はデフォルトで、テストが失敗するとアサーションエラーをスローするようになっています。エラーが発生すると functest 内の該当テストに対応する failure が実行されるようになっています。

デフォルトのアサーションを使用せず、独自に記述することも可能です。以下はテストが失敗した場合のアサーションを独自に記述した例です。

from windmill.authoring import WindmillTestClient

def test():
    client = [[WindmillTestClient]](__name__, assertions=False)

    client.waits.forElement(xpath=u'html/body/center/p/font', timeout=u'3000')
    assert not client.asserts.asse‚ªブジェクトには開始時刻、終了時刻などテスト実行に付随するデバッグ情報が含まれます。

コマンドラインからの変数指定

Windmill のコマンドライン引数のうち使用されなかったものは functest のレジストリに保持されるため、Python のテストコード内からアクセスして利用することが可能です。

from windmill.authoring import WindmillTestClient
import functest

def setup_module(module):
    client = [[WindmillTestClient]](__name__)
    
    client.click(id=u'email')
    client.type(text=functest.registry['email'], id=u'email')
    client.type(text=functest.registry['password'], id=u'pass')
    client.click(value=u"Login")
    client.waits.for[[PageLoad]](timeout=u'60000')

functest.registry['email'] の部分が指定した引数に対応する部分で、実行するときに実際の値に置き換えられます。

上記テストを実行する場合のコマンドライン:
windmill firefox http://www.facebook.com email=abc123@slide.com password=something

JavaScript テスト

Windmill の JavaScript テストはテストを記述したファイルを含む .js ファイルのディレクトリツリーで構成されます。Windmill のサーバー側処理としては、 単純にディレクトリツリーを探索し、その中にあるファイルを配信するたけです。テスト実行の段階で Windmill はテスト対象アプリケーションの window スコープ内にある .js ファイルの内容を評価します。つまりアプリケーション内で何らかの `名前空間` が定義されている場合、特別な設定などを行うことなく JavaScript からそこにアクセスできることを意味します。

JavaScript テストエンジンは Python テストと同じ UI コマンドをサポートしており、JSUnit で使えるものとおなじアサーションが利用可能です(Assert の項を参照)。

テストのセットアップとパース

Windmill はテスト対象アプリケーションの `window` オブジェクトを再帰的に探索、test_ で始まる名前および setup、teardown という名前のオブジェクトを見つけて登録します。

テストフェーズ

setup、test_、teardown という名前はそのテストがどのフェーズに属するかを示しています。同一レベルに複数のオブジェクトがある場合、setup が最初に実行され、続いて test_、最後に teardown が実行されます。つまりこの仕組みを利用して、あるテストを実行する前に毎回必ず setup を実行するようにテスト階層を構成することが可能になります。

テスト実行の際、特定のフェーズのテストオブジェクトを選択的に実行する機能もあります。この機能により、不要なステップを何度もくり返さずに済み、特定フェーズの集中的なデバッグがしやすくなります。

テストを構成する

`require` コマンドは JavaScript モジュール間でのコードを共有、およびテストのロード順決定のために用いられます。windmill.jsTest.require はフレームワークに対し、ファイルのロードと評価の即時実行を強制します。使い方は次の通り。:

windmill.jsTest.require('shared/test_login.js');

`require` のパスは、`require` を記述したファイルの場所にかかわらず、実行する JavaScript テストのトップレベル・ディレクトリからの相対パスで記述しなくてはなりません(この方法は一長一短で、柔軟さに欠ける一方、JavaScript の名前空間とディレクトリやファイル名の関連付けに頭を悩ませなくて済むというメリットがあります)。

`require` メソッドは再帰的に動作するため、他のファイルを `require` しているファイルを `require` することができます。たとえ同じファイルが何度も `require` されても、テストの実行過程でフレームワークが特定のファイルを評価するのは一度だけです。

initialize.js ファイル

Windmill JavaScript にはもうひとつ、ファイルのロードに関する重要なファイルがあります。ロード、評価、パースの何れよりも先に読み込まれ、他の何れの処理よりも先に実行されるファイルで、テストのトップディレクトリに置かれる initialize.js という名前のファイルです。

このファイルはテストで使用するショートカット名を定義したり、テストの名前空間階層の準備が完了するまで一時的にテストオブジェクトを保持する作業用の名前空間を用意するために用いられます。

initialize.js ファイルの内容例:

var BASE_PATH = '/some_path'
var ACCOUNT_PREFS = myApp.base.getPrefs();
var USERNAME = fleegix.cookie.get('username');
var LOGOUT_REDIRECT = 'http://subdomain.mydomain.org/thanks';

var fooNamespace = {};
var barNamespace = {};

initialize.js の内容は他のどのコードよりも先に実行されることを憶えておいてください。

特定の順番でテストを実行する

複数の .js ファイルをテスト用ディレクトリに単純に配置しただけだと、テストがどのような順番で評価、実行されるかは保証されせん。

JavaScript テストエンジンは initialize.js 内で、実行したいテストの名前空間の配列を引数にして `register` コマンドを呼び出すことにより、テストの順番を指定できるようになっています。以下はその例です。

// テストを実行したい順番にこの部分を記述します
var registeredTests = [
‘test_jsonDom’,
‘test_waitForXHR’,
‘test_formBasics’,
‘test_jumBasics’,
‘test_scope’,
‘test_timer’
];

// 実行したい順番をトップレベルのテスト名前空間として登録します
windmill.jsTest.register(registeredTests);

名前空間に記述されたテストは記述した順番に実行されます(ただし setup と teardown だけはそれぞれ最初と最後に実行されます)。

テストのフォーマット

JavaScript テストは2つの方法で実現されます。

  • 関数の実行
  • Windmill コントローラー API のコマンドオブジェクト

これらのテストは単独でも実行可能ですが、複数のテストを配列にセットして一連のものとして実行することも可能です(テストに名前空間を割り当てることもできます。"テストへの名前空間割り当て" 参照)。

単一の関数によるテスト

単一の関数によるテストとは、単純にテスト対象アプリケーションの `window` スコープ内の JavaScript 関数を実行するものです。

以下はその例です。:

function test_fooThing () {
  var foo = 'asdf';
  jum.assertEquals(foo, 'asdf');
  var bar = 'qwer';
  jum.assert[[NotEquals]](bar, 'asdf');
}

var test_getData = function () {
  // Callback function -- sets temp flag to 'returned'
  var success = function (s) {
    var data = eval('('+s+')');
    my[[TestInitData]].tempData = data;
  };
  var url = windmillMain.shared.util.get[[CurrentDir]]() + 'data.json';
  fleegix.xhr.get(success, url);
};

コントローラー API コマンド・オブジェクト

“standard Windmill controller API”: Controller API のアクションはすべて JavaScript テストフレームワークからも実行可能です。

JavaScript と Python によるテストの大きな違いは、アクションの実行に際し JavaScript はサーバーとのやり取りが生じないことです。JavaScript テストは単純にコントローラーオブジェクトのアクションを呼び出すだけのため、実行速度も速くなります。しかし一方でテストの実行が完了するまで成功/不成功のレポートは出力されなくなります。

Asserts

var test_album[[NodeExists]] = { 
  method: "asserts.assertNode", 
  params: {
    id: 'album_9'
  } 
};
var test_album[[NodeText]] =  {
  method: "asserts.assertText",
  params: {
    id: 'album_9',
    validator: 'Power Windows'
  }
};

Waits

test_hasNavigated = {
  method: "waits.forElement",
  params: { 
    id: "form[[PageHeader]]"
  } 
};

注意: これらのコードに含まれる UI コントロールオブジェクトは、対応する .js テストファイル読み込みの際インラインで評価されます。テストコードが評価された時点でページに存在しないオブジェクトを参照することはできません。

JavaScript から直接アクションを呼び出す

UI コマンドオブジェクト・メソッドは JavaScript から `windmill.jsTest.actions` のメソッドとして呼び出すこともできます。この機能は UI 要素の検索を動的に実行したいときや、ページが最初にロードされた段階ではまだ存在しない UI 要素を操作したいときなど役に立ちます。

使用例:

var test_bazThing = function () {
  var nodeId = get[[SomeDomNodeDynamically]]();
  windmill.jsTest.actions.click({ id: nodeId });
}

特殊なメソッド “waits.forJS”

これは 任意の JavaScript が `true` を返すまで待つメソッドです。メソッドに渡す JavaScript は文字列、関数の何れでもかまいません。文字列または関数を params オブジェクトの `js` プロパティとして渡します。

使用例:

test_TempData = { 
  method: "waits.forJS",
  params: { 
    js: function () { 
      return !!windmillMain.tempData;
    }
  }   
};

配列を使ったテストのグループ化

実行すべきアクションや関数が多数あるとき、JavaScript テストエンã失敗のレポートもグループ単位となってしまうことに注意してください(テストに失敗した場合、テストエンジンは配列内のテスト実行を中止して失敗をレポート、次の別のテスト実行に移ります)。

var test_array[[BarThing]] = [
  // Click a button
  { method: "click",
    params: {
      id: "fooButton"
    }
  },
  // Wait around a bit for an element on the new page
  { method: "waits.forElement",
    id: {
      'zoomieNode'
    }
  },
  // Verify the Ajaxy update to the page happened
  function () {
    var node = document.get[[ElementById]]('zoomieNode')
    jum.assertText(node, 'Howdy');
  }
];

JSUnit 互換のアサート

Windmill の JavaScript テストフレームワークは JSUnit 完全互換のアサートを備えており、JavaScript テスト関数の中から “jum” オブジェクトのメソッドとして利用可能です。 Cartão de VisitaBingo

利用可能なアサート:


assertTrue
assertFalse
assertEquals
assertNotEquals
assertLessThan
assertMoreThan
assertNull
assertNotNull
assertUndefined
assertNotUndefined
assertNaN
assertNotNaN
assertEvaluatesToTrue
assertEvaluatesToFalse
assertContains

シンタックスも JSUnit と完全に同じで、オプションのコメントは最初のパラメータとして指定します。:

jum.assert[[NotNa]]N('Totally not a number!', 'Geddy Lee');
=> (Totally not a number!) assert[[NotNa]]N -- <Geddy Lee> (String) expected to be a number but was not a number (NaN).
jum.assert[[NotNa]]N('Neil Peart');
=> assert[[NotNa]]N -- <Neil Peart> (String) expected to be a number but was not a number (NaN).

テストへの名前空間割り当て

Windmill の JavaScript テストは、他の JavaScript 同様名前空間を割り当てることができます。setup/teardown を用いた名前空間の割り当ては、洗練されたテストツリーの構成を可能にします。

以下はテストオブジェクトへの名前空間割り当ての例です。名前空間オブジェクトを test_ で始まる名前にすることで、JavaScript パーサーに対しこのオブジェクトがテストのコンテナであることを示します。

windmill.jsTest.require('shared.js');

test_jsonDom = new function () {
  // Navigate to the main page
  this.setup = windmillMain.shared.test_navMain;

  this.test_getData = function () {
    // Callback function -- sets temp flag to 'returned'
    var success = function (s) {
      var data = eval('('+s+')');
      windmillMain.tempData = data;
    };
    var url = windmillMain.shared.util.get[[CurrentDir]]() + 'data.json';
    fleegix.xhr.get(success, url);
  };
  this.test_wait = windmillMain.shared.util.wait[[ForTempData]];
  this.test_verifyData = function () {
    var data = windmillMain.tempData;
    var albums = data.albums;
    jum.assert[[NotNa]]N(albums.length);
  };
  this.test_writeDom = function () {
    var data = windmillMain.tempData;
    var albums = data.albums;
    var c = $('content');
    for (var i = 0; i < albums.length; i++) {
      var d = $elem('div', {
        id: 'album_' + i,
        innerHTML: albums[i]
      });
      c.appendChild(d);
    }
    jum.assertTrue(c.has[[ChildNodes]]());
  };
  this.test_dom[[NodesActionStyle]] = [
    { method: "asserts.assertNode", params: {id: 'album_9'} },
    { method: "asserts.assertText", params: {id: 'album_9',
      validator: 'Power Windows'} }
  ];
  this.teardown = function () {
    // Clear out the temp data
    windmillMain.tempData = null;
    // Clear the DOM
    $('content').innerHTML = '';
  };
};

tests for the JavaScript test engine には名前空間の割り当てを使用した JavaScript テストのサンプルが数多くあります。

JavaScript テストの実行

JavaScript テストの実行は、以下の何れかの方法を用います。

Python シェルからの実行:
run_js_tests('dir/to/js/tests')

コマンドラインからの実行:

windmill firefox http://yourdomain.com jsdir=./js/test/dir

テストが完了と同時にブラウザも終了させたいときは `exit` オプションを付加:

windmill firefox http://yourdomain.com jsdir=./js/test/dir exit

テストの開発 — 特定のテストまたはフェーズだけを実行する

テストの開発作業において、デバッグのため新たなテストを何度も実行しなければならないときなど、setup を実行させたくないことがあります。JavaScript
テストエンジンは特定のテストやフェーズ(setup、test、または teardown’)だけを実行することもできるようになっています。

特定のテストだけを実行する

特定のテストだけを実行するには、以下のように実行したいテスト名を2番目のパラメータに指定して Windmill の Python シェルから実行します。:

run_js_tests("./fleegix_js/tests", 'test_fleegixEvent.test_foo')

`ns:` プレフィックスを付けると、特定の名前空間に属するテストだけを実行させることができます。

run_js_tests("./fleegix_js/tests", 'ns:test_fleegixEvent')

注意: テストで階層化した名前空間を使用している場合、指定したテストや名前空間の親名前空間にある setup と teardown も実行されます。

特定のフェーズだけを実行する

特定のフェーズだけを実行するには、`run_js_tests` コマンドの3番目のパラメータとしてフェーズ名を(フェーズを複数実行するときはカンマで区切って)与えます。この方法は特定テストの作業中で、setup、test、teardown の流れを一々実行したくないときに役立ちます。

テスト実行の準備ができたら先ず setup を実行:

run_js_tests("./fleegix_js/tests", 'ns:test_fleegixXhr', 'setup')

その後、必要なテストを必要な回数実行:

run_js_tests("./fleegix_js/tests", 'ns:test_fleegixXhr', 'test')

一度 teardown を実行した後、すべてを再実行:

run_js_tests("./fleegix_js/tests", 'ns:test_fleegixXhr', 'teardown')
run_js_tests("./fleegix_js/tests", 'ns:test_fleegixXhr', 'setup, test, teardown')

コマンドラインで特定のテストを指定して実行

特定のテストやフェーズの実行は `jsphase` と `jsfilter` オプションを使ってコマンドラインから指定することも可能:

windmill firefox http://yourdomain.com jsdir=./js/test/dir jsfilter=ns:test_fleegixXhr jsphase=setup

You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.