PhantomJSエンジンを使用したExtJS / Senchaコンポーネントとアプリケーションのテスト

PhantomJSは、コンソールモードでWebページをロードし、JavaScriptを実行し、DOM、Canvas、SVGを完全に操作できるグラフィカルインターフェイスのないWebKitエンジンアセンブリです。 PhantomJSの主なアプリケーションの1つは、自動化されたユーザーインターフェイスの機能テストです。 PhantomJSは、JavaScriptおよびWebページをテストするためのさまざまなフレームワークと統合されています。 ExtJS / Senchaで記述された個別のコンポーネントとアプリケーション全体をテストするために、標準のPhantomJS機能に基づいて何ができるかを見てみましょう。 この記事では、サードパーティのJavaScriptライブラリに基づいてコードをテストする方法を示すテストフレームワークの最も簡単な準備を提供します。 この記事に記載されているすべてのコードはGitHubで入手できます







コンポーネントテスト





標準のExt.form.ComboBoxドロップダウンリストを拡張する単純なコンポーネントを検討してください。 これには、構成パラメーターmonthsを使用してカスタマイズ可能な月のリストが含まれている必要があります。これは、ゼロから始まる表示された月の番号を含む配列です。 たとえば、配列が1、3、および5の数字で構成されている場合、ドロップダウンリストには2月、4月、6月が表示されます。



Ext.define('MonthComboBox', { extend : 'Ext.form.ComboBox', alias : 'widget.monthcombo', store : Ext.create('Ext.data.Store', { fields : [ 'num', 'name' ] }), queryMode : 'local', displayField: 'name', valueField : 'num', allMonths : ['', '', '', '', '', '', '', '', '', '', '', '' ], months: [], initComponent: function() { /** *     ,      {@link MonthComboBox#months} */ for (var i = 0; i < this.months.length; i++) { this.store.add({ num : this.months[i], name: this.allMonths[this.months[i]] }); } this.callParent(arguments); } });
      
      







コンポーネントがテストされるHTMLページExtjsTesterPage.htmlを作成します。 ページにExtJSスタイルとスクリプトを含めるには、少なくともext-all.cssおよびext-all.jsファイルをインポートします。 この目的のために、CacheFlyでホストされているExtJS 4.0.2aのバージョンを使用します。



 <!DOCTYPE html> <html> <head> <title>ExtJS Test Page</title> <link rel="stylesheet" type="text/css" href="http://extjs.cachefly.net/ext-4.0.2a/resources/css/ext-all.css"> <script type="text/javascript" src="http://extjs.cachefly.net/ext-4.0.2a/ext-all.js"></script> </head> <body></body> </html>
      
      







テスト用の小さなサンプルフレームワークを準備します。 これはExtjsTester.jsファイルであり、PhantomJSに渡され、テストとして引数としてスクリプトの名前を取ります。 最初に、コマンドライン引数の存在を確認します。



 if (phantom.args.length == 0) { console.log('      '); phantom.exit(1); }
      
      





このコードスニペットを例として使用して、PhantomJS APIの2つの重要な要素を説明できます。 まず、パラメーターをスクリプトに渡します。phantom.argsはコマンドライン引数の配列です。 2番目のAPI要素はphantom.exit()メソッドの呼び出しです。これにより、PhantomJSが終了し、指定されたエラーコードが返されます。



モジュールを接続してファイルシステムを操作します。ファイルシステムは実行時にスクリプトをロードする必要があります。 fsモジュールにはかなり広範な機能があり、ファイルの検索、読み取り、書き込みが可能です。

 var fs = require('fs');
      
      







PhantomJSには統合されていますが、簡単にするために、JavaScriptの機能テスト用に既存のライブラリを接続しませんでした。 assert例外の標準クラスを宣言します。

 function AssertionError(message) { this.message = message; } AssertionError.prototype.toString = function() { return 'AssertionError: ' + this.message; };
      
      







テストの目的で、標準のPhantomJSクラスであるWebPageのラッパーを作成します。



 function TestPage(scriptUnderTest, testFun) { //      if (!fs.exists(scriptUnderTest)) { console.log("File " + scriptUnderTest + " not found"); phantom.exit(1); } var me = this; //   this.page = require('webpage').create(); //           this.page.onConsoleMessage = function (msg) { console.log(msg); }; //    this.page.open("ExtjsTesterPage.html", function() { //    me.page.injectJs(scriptUnderTest); //   ExtJS     me.waitForExtReady(function() { me.doTest(testFun); }); }); }
      
      







コンストラクターパラメーターscriptUnderTestは、たとえばExtJSコンポーネントのコードなど、テストするスクリプトを含むファイルの名前です。 以前にインポートしたfs依存関係を使用して、このファイルの存在を確認し、WebPage#injectJs関数を使用して、以前に作成したExtjsTesterPage.htmlファイルからダウンロードしたテストページに挿入します。



WebPage#onConsoleMessageメソッドに注意する価値があります。 指定しない場合、ページはサンドボックスで実行されるため、ページで発生したすべてのコンソール出力は失われます。



testFunパラメーターは、将来のテスト関数です。 ExtJSコンポーネントを正しくテストするには、ExtJSフレームワークが完全にロードおよび初期化されていることを確認する必要があります。 Ext.onReady()メソッドはこれを対象としていますが、この場合、ページがサンドボックスで実行されるという事実によってその使用が妨げられ、ページ内のコードの転送はWebPage#評価メソッドを使用してのみ可能です。 したがって、ExtJSがロードすることを期待するメソッドをTestPageクラスに提供します。



 TestPage.prototype.waitForExtReady = function(fun) { var me = this; console.log(' ExtJS...'); var readyChecker = window.setInterval(function() { var isReady = me.page.evaluate(function() { return Ext.isReady; }); if (isReady) { console.log('ExtJS .'); window.clearInterval(readyChecker); fun.call(me); } }, 100); };
      
      







ExtJSが完全にロードされると、doTestメソッドは次のように実行されます。



 TestPage.prototype.doTest = function(testFun) { try { //    testFun.call(this); phantom.exit(0); } catch (e) { console.log(e); phantom.exit(1); } };
      
      







テスト関数の呼び出しと、テスト関数の結果に対応する戻りコードでのPhantomJSの完了を実装します。



結論として、ページのコンテキストでコードをアサートおよび実行するためのいくつかの規則をTestPageクラスに追加します。



 TestPage.prototype.evaluate = function(fun) { return this.page.evaluate(fun); }; TestPage.prototype.assert = function assert(test, message) { if (!test) { throw new AssertionError(message); } }; TestPage.prototype.evaluateAndAssertEquals = function(expectedValue, actualFun, message) { this.assert(expectedValue === this.evaluate(actualFun), message); };
      
      







evaluateメソッドは、渡された関数の実行をラップされたWebPageオブジェクトに委任します。 assertメソッドは、失敗するとAssertionErrorを使用して汎用診断ステートメントを実行します。 evaluateAndAssertEqualsメソッドは、ページのコンテキストでの関数実行の結果を期待値と比較します。



ExtjsTesterファイルの最後の行は、テストスクリプト自体を読み込んでいます。これは、コマンドラインパラメーターを介して渡されます。 このためにファイルシステムを操作するためにモジュールを使用します-スクリプトを文字列としてロードし、evalを介して実行します。



 eval(fs.read(phantom.args[0]));
      
      







テストスクリプト自体は、MonthComboBoxTest.jsファイルです。

 new TestPage("MonthComboBox.js", function() { //   MonthComboBox      this.evaluate(function() { Ext.widget('monthcombo', { months : [1, 2, 5], renderTo: Ext.getBody() }); }); // ,        this.evaluateAndAssertEquals(3, function() { return Ext.ComponentQuery.query('monthcombo')[0].store.getCount(); }, "Wrong element count"); });
      
      







ここで、TestPageラッパーオブジェクトを作成し、テスト対象のコンポーネントのスクリプトとテスト用の関数を渡します。 その中で、最初に3か月のみを含むような構成でページ上にコンポーネントを作成してから、計画が正しいこと、コンポーネントが実際にページに存在すること、そのストレージに正確に3つの要素が含まれていることを確認します。



テストを実行するには、次のコマンドを使用します。



phantomjs ExtjsTester.js MonthComboBoxTest.js









アプリケーションテスト





まず、アプリケーションをテストするために、フレームワークを少し改良する必要があります。 テスト機能は、アプリケーションの起動が完了したら実行する必要があります。 これを判断する1つの方法は、ページ上のビューポートコンポーネントの存在を確認することです。 通常、TestPage#waitForExtReadyメソッドと同様に、TestPageクラスに次のメソッドを追加します。



 TestPage.prototype.waitForViewport = function(fun) { var me = this; console.log('  Viewport...'); var launchedChecker = window.setInterval(function() { var isLaunched = me.page.evaluate(function() { return typeof Ext.ComponentQuery.query('viewport')[0] !== 'undefined'; }); if (isLaunched) { console.log('Viewport .'); window.clearInterval(launchedChecker); fun.call(me); } }, 100); };
      
      







また、testPageオブジェクトのコンストラクターにwaitForViewportパラメーターを追加します。この値は、テスト関数が実行される前に、Viewportが表示されるまで待つ必要があることを意味します。 テスト関数が呼び出されるフラグメントをわずかに変更します。

 me.waitForExtReady(function() { if (waitForViewport) { me.waitForViewport(function() { me.doTest(testFun); }) } else { me.doTest(testFun); } });
      
      







簡潔にするために、ここではテスト対象のアプリケーションのすべてのコードを示しません。GitHubでそれを見ることができます 。 アプリケーションは、ボタンとこのボタンをクリックすると表示されるウィンドウを備えたメインフォームで構成されることのみを説明します。 メインフォームとウィンドウは、さまざまなコントローラーによって提供されます。コントローラー間の通信は、アプリケーションレベルのメッセージ(Ext.app.Application#fireEvent)によって編成されます。 以下は、ボタンがクリックされたときにポップアップウィンドウが表示されることを確認するテストコードです。



 new TestPage("app.js", function() { //     this.evaluate(function() { Ext.ComponentQuery.query('mainform > button[action=popup]')[0].btnEl.dom.click(); }); // ,      this.evaluateAndAssertTrue(function() { return typeof Ext.ComponentQuery.query('popupwindow')[0] !== 'undefined'; }, "Popup window not opened"); }, true);
      
      







コンポーネント要求 'mainform> button [action = popup]'は、アプリケーションのメインフォームのボタンに対応しています。 クエリ「popupwindow」はポップアップに対応しています。 TestPageコンストラクターの3番目のパラメーター(true)は、テスト関数を実行する前にビューポートが表示されるのを待つ必要があることを意味します。 テスト機能は同じ方法で開始します。

phantomjs ExtjsTester.js AppTest.js









この記事では、PhantomJSエンジンに基づいてExtJS / Senchaでコンポーネントとアプリケーションをテストする方法を説明し、テストフレームワークの近似ブランクを開発します。これは、他のJavaScriptライブラリのテストにも同様に使用できます。 結論として、このような機能テストはWebKitエンジンで実行されているブラウザーに限定されることに注意してください。 ただし、このエンジンが最新のWeb標準に高度に準拠していること、および許容できるレベルの抽象化とクロスブラウザーライブラリExtJS(Sencha)を考えると、アプリケーションのロジックまたはコンポーネントPhantomJSの機能をテストするには十分であると結論付けることができます。



テストコンポーネントとアプリケーションのコード、および記事に記載されているテストフレームワークは、 GitHubで入手できます






All Articles