アシアルブログ

アシアルの中の人が技術と想いのたけをつづるブログです

Protractorでハイブリッドアプリを自動テスト!

こんにちは、吉田です。


アプリの開発にはテストがつきものですよね。
納品前に最終的なテストを行うのはもちろんですが、それに加えて開発中も気軽にテストを実行できる環境があれば、既存コードのリファクタリングや使用しているフレームワークのバージョンアップなども安心して行えます。


内部のロジックをクラスやメソッド単位で内側からテストするユニットテストの自動化は行われている場合も多いかと思いますが、アプリの場合は画面遷移や要素の表示・挙動が占めるウエイトもかなり大きいので、「Aのボタンを押したらBの画面に遷移し、Cという要素が表示されている」といったような外側からのテストも大事になってきます。
その辺りのテストは手動で行う場合もあるかもしれませんが、フレームワークのバージョンアップやリファクタリングのたびに手動で全ページを表示して動作確認を行うのはつらすぎる。。
外側からのテストも自動化したい・・!!


というわけで今回は
「Cordova + AngularJS + Onsen UI でハイブリッドアプリを作り、それをProtractorで外側から自動テストする」
という内容をご紹介してみたいと思います!


※ちなみに今回アプリを実行するのは実機ではなくブラウザなので、実機で行う厳密な表示テストに替わるものではありませんが、JavaScriptだけで手軽に画面遷移や要素の有無を確認する自動テストを書きたい!という時には役に立つ場面があるかなと思います。


■Cordova + Onsen UIでテスト対象のアプリを作成


まずはテスト対象となる簡単なアプリを作成します。
今回私の記事ではテストを行う部分がメインですので、対象アプリの作成については順を追ったコマンドだけ羅列していきます。
(アプリ作成の段階については別の記事:AngularJS + Onsen UIで始めるPhoneGapアプリケーションで詳しく解説されていますので、ご興味のある方はぜひどうぞ!)


ではターミナルを開いてアプリを作っていきましょう!




# cordovaコマンドが入っていない方のみ
sudo npm install -g cordova

# cordovaアプリの作成
cordova create test-app com.example.testapp TestApp
cd test-app
cordova platform add ios

ここまでで、Cordovaアプリのファイル群ができ、iOSシミュレータや実機で動くようになりました。
シミュレータや実機で動かせる環境がある場合は、cordova run iosで実行できるはずです。


しかし今回の自動テストはPCで立ち上げるブラウザを対象に行いますので、iOSシミュレータや実機の環境がない方でも実行できます!
ではさっそくブラウザで動作確認してみましょう。





cordova serve ios


"Static file server running on port 8000 (i.e. http://localhost:8000)"
というメッセージを確認して、http://localhost:8000 にアクセス。








こんなかんじの画面が出ればOK!
cordova serveをCtrl-Cで停止して、今度は表示する内容をOnsen UIのテンプレートに差し替えてみましょう。
wgetコマンドが入っていない場合は、こちらのzipファイルを落として解凍して手動で差し替えても問題ありません。





wget http://ja.onsenui.io/OnsenUI/project_templates/onsen_master_detail.zip
unzip onsen_master_detail.zip -d onsen
rm -r www
cp -r onsen/www .
rm -r onsen
rm onsen_master_detail.zip


もう一度実行してみます。





cordova serve ios


http://localhost:8000/ios/www/ でこんな画面が表示されて動いていればOK!








これで、テスト対象アプリの準備が出来ました。


■自動テスト環境の準備


今回はブラウザを自動で操作して外側からのテスト、いわゆるエンドツーエンドテストを行ってみたいと思います。
AngularJSがエンドツーエンドテストツールとして推奨している、Protractorというライブラリを使ってみましょう!





npm install protractor # Protractorのインストール
node_modules/.bin/webdriver-manager update # Seleniumツールのインストール


準備ができたら、プロジェクトのルートディレクトリ(test-app/)直下にテスト用のディレクトリを作ります。





mkdir spec
cd spec


ここに、conf.jsというProtractorの設定ファイルを作ります。
内容は以下のとおり。





exports.config = {

  // seleniumサーバーのURL
  seleniumAddress: 'http://127.0.0.1:4444/wd/hub',

  // テストコードを書いたファイル
  specs: [
      'spec.js'
  ],

  // 使用するブラウザ
  capabilities: {
    'browserName': 'chrome'
  }
};


テストを書くファイルは、同じくspecディレクトリの中にspec.jsという名前で作成します。
JSテストフレームワークであるJasmineを使用するので、Jasmineの書き方でテストを記述します。





describe('エンドツーエンドテスト', function() {

    it('最初の画面のテスト', function() {

        // cordova serveのURLを開く
        browser.get('http://localhost:8000/ios/www/');

        // <ons-navigator>タグが表示されている
        expect($('ons-navigator').isDisplayed()).toBe(true);

        // <ons-toolbar>の中のテキストが"Master Detail"と一致する
        expect($('ons-toolbar').getText()).toEqual('Master Detail');
    });
});


specディレクトリの中にconf.jsとspec.jsが準備できましたか?
ではいよいよ実行してみましょう!!


プロジェクトのルートディレクトリ(test-app/)を開いたターミナルの画面を3つ用意してください。
それぞれで





node_modules/.bin/webdriver-manager start # 1つ目のターミナルでSeleniumサーバーを起動
cordova serve ios # 2つ目のターミナルでCordovaアプリをブラウザで実行
node_modules/.bin/protractor spec/conf.js # 3つ目のターミナルでテスト実行


を上から順に実行すると・・・
勝手にChromeが立ち上がって、テストが実行されましたでしょうか?
テストを実行したターミナルに、


1 test, 2 assertions, 0 failures


と表示されていればめでたく成功です!


■テストを増やす


やっと自動でテストを実行できるようになったので、ページ遷移をテストしてみましょう!
リストをクリックして遷移先のページの内容をチェックしていく処理を追記します。





describe('エンドツーエンドテスト', function() {

    it('最初の画面のテスト', function() {

        // cordova serveのURLを開く
        browser.get('http://localhost:8000/ios/www/');

        // <ons-navigator>タグが表示されている
        expect($('ons-navigator').isDisplayed()).toBe(true);

        // <ons-toolbar>の中のテキストが"Master Detail"と一致する
        expect($('ons-toolbar').getText()).toEqual('Master Detail');
    });

    // ----------------- ここから追記 ---------------------

    it('画面遷移 リスト1つ目', function() {
        // リストの1つ目をクリック
        $$('ons-list-item').get(0).click();

        // 画面が変わるのを待つ(1秒)
        waits(1000);
        runs(function() {
            expect($('ons-list-header').getText()).toEqual('Item Information');
            expect($('ons-list-item strong').getText()).toEqual('Item 1 Title');
            $('ons-back-button').click(); // Backボタンをクリック
        });
        waits(1000);
    });

    it('画面遷移 リスト2つ目', function() {
        // リストの2つ目をクリック
        $$('ons-list-item').get(1).click();

        // 画面が変わるのを待つ(1秒)
        waits(1000);
        runs(function() {
            expect($('ons-list-header').getText()).toEqual('Item Information');
            expect($('ons-list-item strong').getText()).toEqual('Another Item Title');
            $('ons-back-button').click(); // Backボタンをクリック
        });
        waits(1000);
    });

    it('画面遷移 リスト3つ目', function() {
        // リストの3つ目をクリック
        $$('ons-list-item').get(2).click();

        // 画面が変わるのを待つ(1秒)
        waits(1000);
        runs(function() {
            expect($('ons-list-header').getText()).toEqual('Item Information');
            expect($('ons-list-item strong').getText()).toEqual('Yet Another Item Title');
            $('ons-back-button').click(); // Backボタンをクリック
        });
        waits(1000);
    });
});


合計4つの画面の遷移と、遷移先ページのテキストのテストは無事に成功しましたでしょうか?
成功するとこんな感じになります!(アニメーションGIF)



画面遷移のときは、画面の切り替えを待つためにclickのあとにwaitsを挟むのがコツです!
ブラウザの画面が自動で動いていくのは面白いですね!


あとは今の冗長なテストコードをリファクタリングしたり、実装に合わせてテストも成長させていきましょう!


■テストの記法について知りたい場合


describe, it, expectなどはJasmineのメソッド
$, getText, clickなどはProtractorのメソッドになります。
どちらも今回使った他にまだまだ機能があり、特にProtractorはAngularJSが推奨しているだけあって、たとえばng-modelやng-bindの値を使って要素を指定することなどもできます。


また、今回はボタンでの画面遷移と要素の確認だけでしたが、他にもポップアップしたアラートの操作やフォームへの入力、スクリーンショットをとることなどもできますので、その辺りも気になる方は以下のドキュメントをご参照ください!


Jasmine(1.3) ドキュメント
Protractor ドキュメント


もしくはリクエストいただければその辺りまで踏み込んだブログを書くかもしれません!


■おわりに


Cordovaアプリの画面遷移チェック自動化について、私が試してみた方法をご紹介させていただきました。


ちなみに今回の記事の手法をひと通り試し終わったあとに見つけたのですが、実機で自動エンドツーエンドテストができるappiumというオープンソースフレームワークもあるようです。
とても気になっているのでそちらも是非試してみたいです!


他にも色々な方法・ノウハウがあるかと思いますので、是非教えていただけると嬉しいです!