HOME > 開発者向けBLOG > Sencha Blog >  Siesta でExt JSアプリのTDDを効率化する

Technology Note 開発者向けBLOG

Sencha Blog

Siesta でExt JSアプリのTDDを効率化する

こんにちは、ゼノフィnakamuraです。

この記事は、US Sencha社ブログ Using Siesta to Get Efficient TDD for your Ext JS Apps を翻訳したものです。

Siesta テストのセットアップの最適化

Siesta と Ext JS のテストについて記事を更新してから幾分か時間が経ちました。それ以降、Siesta には コードカバレッジ、イベントレコーダー、スクリーンショット、クラウドテストプロバイターとの統合などなど、新しい機能がいくつか追加されました。この記事では、よりテストを最適に行えるような情報や裏技をお伝えします。

テストを行う時間を短縮できるということは、フィードバックも早くなるということであり、開発サイクル全体のスピードアップにも繋がるということです。また、クラウドテストサービスを使用しているのであれば、時間を短縮できればその分コストも抑えられるということになります (テストに使用する時間で費用が変わるため )。

JavaScript コードベースをテストする上で時間を短縮できるテクニックを紹介していきましょう。

サンドボックスを使うべきか、使わざるべきか

Siesta 2.1.0 のリリースでは、大きな変化がありました。 sandboxing を使うかは選択できるようになりました。これは、テストセットアップ時間が低いもしくは無かったりする、ユニットテストの大きなグループには非常に影響があります。ユニットテストは一般的にはロジックに焦点があたっているため、DOM が準備できているかを待たなかったり、マウスクリックをシミュレートしなかったりするので、非常に速く走ります。

サンドボックスとはどういう意味なのでしょうか? Siesta は最初のリリースから、テストは独自の iframe の中で行われてきました。これがサンドボックスです。 この Siesta の機能はテストが始まる時にフレッシュなコンテキストが得られることを保証し、グローバルブラウザプロパティの変更やグローバル変数の定義付けなどによって、他のテストの邪魔をしないようにします。 もう一つの大きな利点は、テスト後に手動でクリーンナップする必要がありません。つまり、サンドボックスは簡単にテストを書けるようになることで、テスト開発者の参入障壁を減らすことができます。しかし、このアプローチには不利な点もあります。サンドボックステストが行われると、裏では次のようなことが行われています。

  1. Siesta がテストのために新しい iframe を作り、ページに追加する。
  2. プリロードで定義付けされている JavaScript や CSS リソースが iframe に注入されます。
  3. Siesta はリソースがロードされ、DOM の準備ができるのを待ち、Ext JS が onReady を発火するのを待ちます。
  4. Siesta がテストコードを実行します。
  5. Siesta が iframe を破棄します。

これは、かなりの時間をセットアップで失うことになってしまい、特に非常に速いスピードで実行されるユニットテストには影響が大きいです。上記で解説されているセットアップは秒単位で測られます。しかし、それに比べユニットテストは 100ms もしくはそれ以下で終えることができます。最悪の場合、セットアップの時間はテストコードを実行する時間に10〜20掛けた数字になってしまうということです。つまり、改善の余地があるということです。

But Beware of Global Variables! グローバル変数に注意!

テストのグループが同じプリロードファイルを共有していて、どれもがグローバルステートを変更しない場合は、すべてのテストスクリプトは同じ iframe を使用できるため、セットアップ時間を短縮できます。これは、Siesta ハーネスで、サンドボックスオプションを無効化することで行うことができます。Siesta は別のプリロードがあるテストの場合は自動的に新しいサンドボックスでテストを始めるので、開発者が気にすべきことはグローバルステートを変更しないという点だけです。

グローバルステートを変更とはどういうことかをここで見せましょう:

// test1.t.js
StartTest(function(t) {
    // "Helper" is a config object
    Helper = { prop : 'value' }
 
    // setting some global config before the test
    MyApp.someConfig = "someValue"
 
    // registering some store
    var store = new Ext.data.Store({ storeId : 'store' })
})

見ての通り、最初のテストファイルは2つの新しいグローバル変数を作成し、MyAppのプロパティを変更します。これは続けてテストが実行された場合に問題を起こしてしまいます(以下参照)。

// test2.t.js
StartTest(function(t) {
    // "Helper" is an ExtJS model class
    Ext.define('Helper', { .... })
 
    // FAIL: the "MyApp.someConfig" is expected to have a default value
    // but it has been changed during execution of test 1
    t.is(MyApp.someConfig, 'defaultValue', "`someConfig` has a default value")
 
    // The store with id `store` is kept from the 1st test
    t.notOk(
        Ext.data.StoreManager.get('store'),
        "Should be no store with id `store`"
    )
});

見ての通り、サンドボックスなしでは簡単なコードでも大きな問題に繋がってしまう場合があります。これらの多くの問題はグローバル変数やシングルトンの変更と関連していて、各テストの後に追加のクリーンナップを行うことで防ぐことができます。 もし共有ステートに注意を払い、テストがメインのテスト関数内のローカル変数のみ使用していれば、サンドボックスを無効化し、テストのパフォーマンスを著しく向上させることが可能となります。

パフォーマンスブースト

以下はサンドボックスを有効/無効にして、68のグループのユニットテストを行っているスクリーンショットです。なぜ我々がこの変更について興奮しているかは、自分の目で確かめてみてください:

Sandbox enabled (old behavior)

Sandbox disabled

このテストグループに関しては、テストパフォーマンスのスピードは、サンドボックスが有効化されている状態の15倍早いことがわかります。Bryntum では、各コミットの前に多くのユニットテストを実行するため、サンドボックスなしでは実行可能ではありません。

UIテストのスピード向上

ユーザーインプットをシミュレートしている場合、Ext JS / ブラウザ API をシミュレートイベントの代わりに使うことでテスト時間を短縮できる場合もあります。イベントシミュレーションは多くの時間を使いますが、より簡単で早い代替え案が存在する場合もあります。以下の状況を考えてみてください。もしユーザー名とパスワードが必要なログインフォームがあったとします。ログインをシミュレートした場合、以下のテストスクリプトを使用できます。

t.chain(
    { click : '>> textfield[fieldLabel=Name]' },
    { type : 'Mike' },
    { click : '>> textfield[fieldLabel=Password]' },
    { type : 'Qwerty123' },
    { click : '>> window[title=Login] button' }
)

このスクリプトはマウスを username テキストフィールドに動かし、クリックし、名前をタイプし、password フィールドにタイプし、ログインボタンをクリックさせます。より最適化されたバージョンは以下となります:

t.cq1(‘textfield[fieldLabel=Name]).setValue(‘Mike’);
t.cq1(‘textfield[fieldLabel=Password]).setValue('Qwerty123');
t.click(>>window[title=Login] button’);

各行は同期的に実行されるため、少しだけ速いです。もしテキストフィールドの実際のキーストロークに関心がないのであれば、上記のバージョンは良い代替え案となります。上記のコードをこのブログ記事のために実験的に行いました。イベントシミュレーションを使用した最初のバージョンは約1.5秒かかり、Ext JS の setValue API を使用したバージョンは 29ms でした。

オートメーションでテストを実行する際に Siesta UI を無効化する方法

コマンドラインからテストを実行する場合、Siesta UI をハーネスページにレンダーする必要はありません。むしろ、Ext JS をロードする必要も全くないので、ブラウザがロードし、パースし、ext-all-debug.js ファイルを実行し、誰も見ることのない UI をレンダーすることを省略できます。これは、ハーネスファイルの HTML ラッパーを複製し、そこから Ext JS を除外すれば可能です。そして、オートメーションランチャーで通常のラッパーではなく、そのラッパーを指定すればよいだけです。

オートメーションのために、Ext JS がロードされていない Harness.html ファイル:

<!DOCTYPE HTML> 
<html>
    <head>
        <meta http-equiv="X-UA-Compatible" content="IE=edge" >
        <meta http-equiv="content-type" content="text/html; charset=UTF-8">
 
        <script type="text/javascript" src="/siesta/siesta-all.js"></script>
        <script type="text/javascript" src="index.js"></script>
    </head>
    <body>
    </body>
</html>

テスト間の一時停止

IE のレガシーバージョンにかかる負荷を軽減するため、Siesta は各テストスクリプト間に一時停止を設けています。この一時停止のデフォルト値は、3秒となっています。この一時停止は、古い IE ブラウザが実行時にゴミを収集するために必須な時間であることは確認しています。その他のブラウザでは、この値を1秒、もしくは0秒に減らしても問題ありません。これは –pause command ラインオプションを使えば変更することができます。こにれより、各テストで3秒短縮することができ、全体で見れば大きなパフォーマンスの向上と言えるでしょう。

この設定は、もちろんこの設定はテストが安定した結果を出している時にのみ変更すべきです。 テストの安定性というのはスピードよりも重要なことであり、コード変更をしていないのに突然テストが失敗に終わるというのはテスト開発者としてはあってはならないことです。

結果

この記事で紹介された情報や裏技を使用すれば、簡単に数秒、または数分、コマンドラインからテストを走らせる場合に短縮することができます。これにより、より早い開発サイクルを生むことができ、あなたのコードベースがどのような状態か、より早いフィードバックを得ることができます。これらの情報を元にテストを最適化した場合、もしくはあなたが使っているパフォーマンスが向上する別の技などあれば、そういった経験を下のコメントセクション、もしくは Bryntum フォーラム で共有してください。

PAGETOP