アシアルブログ

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

タスクランナーとの親和性を向上。AppiumをNode.jsで動かす

前回までのコードはRubyで書いていましたが、MonacaアプリはHTML5/JavaScriptが主な利用言語になります。そこで今回はアプリを動かすスクリプトをNode.jsベースに書き直してみます。



これがうまく動けば、Gulpのようなテストランナーと組み合わせることで、コードの変更からテストまでがとてもスムーズになるはずです。DevOpsを積極的に進める上でも役立つのではないでしょうか。



Appiumのインストール、起動までは前回までの記事を参照してください。



Node.jsの設定



Node.jsについてはあらかじめインストールされていることとします。まだ未インストールの方はNode.jsよりダウンロード、インストールしてください。



まずテストスクリプトを書くフォルダを作成します。





mkdir appium_test
cd appium_test
npm init


そして必要なライブラリをインストールします。





npm install wd --save


これで準備は完了です。テストをスクリプトを書きます。まずAppiumに接続するまでの処理です。





"use strict";

var wd = require("wd");
var browser = wd.promiseChainRemote("0.0.0.0", 4723);


今回はローカルにAppiumサーバを立てています。そして接続する際の情報を定義します。今回はiOSシミュレータ向けに書いています。





var desired = {
	"appium-version": "1.0",
	platformName: "iOS",
	deviceName: "iPhone 6 Plus",
	app: "/Monaca_project_path/platforms/ios/build/emulator/AppiumTest.app"
};


接続がうまくいった場合は、アプリの起動した状態になっているはずです。





browser.init(desired).then(function() {
  // この中にテストの処理を書きます
});


実際の処理ですが、XPathでエレメントを取得しなければなりません。これがとてもネストが深く、分かりづらいのでGUI版Appiumのインスペクタを使うのをお勧めします。Recordボタンを押して、要素をクリックすると、そこまでのXPathが表示されます。





Appium Inspector



そしてXPathが分かったらコードに反映していきます。例えば最初の画面のHelloWorld!を知りたい場合は次のように書きます。





browser.elementByXPath("//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIAStaticText[1]").text().then(function(text) {
  console.log(text);  // -> HelloWorld!
});


全体としてPromiseで作られていますので、text()メソッドを実行しても {status: “pending”} が返ってくるだけです。then()でつないでテキストを受け取る必要があります。



そして非同期ということもあって、テキストの取得とクリックなどの処理をそのまま書いてしまうと、text()メソッドが間に合わずに画面遷移がはじまってしまいます。そこで、一画面ずつ順番に処理を行っていきます。





// テキストを取得
browser.elementByXPath("//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIAStaticText[1]").text().then(function(text) {
  console.log(text);
  // タップ処理
  browser.elementByXPath("//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIALink[1]/UIAStaticText[1]").click().then(function() {
    // タップした後の処理
  });
});


最後にアプリを終了しますが、この時も非同期処理で行われているのを意識しないといけません。





browser.elementByXPath("//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIALink[1]/UIAStaticText[1]").click().then(function() {
  // 処理を遅らせないといきなり終了してしまいます
  setTimeout(function() {
    browser.quit();
  }, 5000);
  // ここに browser.quit(); を書くといきなりアプリが終了します
  // browser.quit();
});


実際に実行した時の動画です。





テストコードを実行



最後に全体のコードです。






"use strict";

var wd = require("wd");

var desired = {
	"appium-version": "1.0",
	platformName: "iOS",
	deviceName: "iPhone 6 Plus",
	app: "/Volumes/SD/Dropbox/DevRel/Monaca/AppiumTest/platforms/ios/build/emulator/AppiumTest.app"
};

var browser = wd.promiseChainRemote("0.0.0.0", 4723);

browser.init(desired).then(function() {
	browser.elementByXPath("//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIAStaticText[1]").text().then(function(text) {
    console.log(text);
    browser.elementByXPath("//UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAWebView[1]/UIALink[1]/UIAStaticText[1]").click().then(function() {
      setTimeout(function() {
        browser.quit();
      }, 5000);
    });
  });
}).done();


注意点



JavaScriptで書く時の注意点としては、



  1. XPathで書く必要がある(ないかも知れませんがクラスでどう指定するのかが分かりませんでした…)
  2. 全体的に非同期処理


となっています。XPathは慣れればさほど難しくないかも知れませんが、Appiumのインスペクタを積極的に活用した方が良いでしょう。ただし画面の変化に対して弱くなってしまうように見えます。非同期についてはネストが深くなってしまう傾向がありますが、テストを適切に分離することで深くなりすぎるのを防げるかと思います。



JavaScriptを使う利点としてはRubyiOSをテストする際にあった落ちるエラーが出なかったので、テストの品質が高くなりそうです。また、Appium自体がNode.jsでできているので親和性が高いかと思います。






Web開発ではGulpを使ってテストを定期的に実行するのが当たり前になっています。Monacaを使ったハイブリッドアプリ開発においても同じようにテストを細かく行うことで品質の高いアプリ開発ができるようになるでしょう。

MonacaアプリのUIテストに。Appiumを試す(Android編)

前回のiOSシミュレータに続いて今回はAndroidエミュレータを使ってAppiumを試してみたいと思います。AppiumはWeb開発時のUIテストでよく知られているSeleniumiOS/Androidアプリに対応させたソフトウェアです。





Appiumのサイト



今回はこのAppiumをMonacaアプリ(Android)で動かす方法について紹介します。



CLIMonacaの準備



Appiumはローカルで動かしますのでMonaca.ioではなくCLI版のMonacaを使います。そのためnode.js/npmはインストールされているものとします。





$ npm install monaca -g


インストールしたらログインを行います。





$ monaca login



ログインしたら新しいプロジェクトを作成してみましょう。今回はHelloWorldアプリにしました。





$ monaca create Hello
Which project template do you want to use?

1: Hello World App
2: Minimum Template
  :
21: NIFTY Cloud mobile backend
22: Onsen UI Minimum Template for Universal App

Type number>1
Project created successfully.


Cordovaコマンドのインストール



次にCordovaコマンドをインストールして、適当なプロジェクトを作成します。





$ npm install cordova -g
$ cordova create HelloCordova
$ cd HelloCordova


今回はAndroidをプラットフォームとして追加します。





$ cordova platform add android


そうするとこのようなファイル構成になります。





Cordovaプロジェクトのファイル構成



まずCordovaプロジェクト上でビルドをします。





$ cordova build android


そうすると platforms/android/src 以下に com というディレクトリが作られているはずです(ない場合はMonacaプロジェクトのAndroidManifest.xmlをCordova上のものに差し替えた後、cordova build androidMonacaプロジェクト上で実行してください)。その後、platforms/android 以下のファイルを Monacaプロジェクトの platforms/android 以下に移動します。Monacaアプリでも同名のディレクトリもありますので、元々入っているものは消さずに、その中身は移動するようにしてください(res以下のXMLファイルなど)。ファイルの配置が変わってしまうとMonaca IDEでビルドできなくなる可能性があります。Appiumで使うコードは元のコードから分離し(Gitでcloneするなど)、元々のMonacaプロジェクトには影響しないようにしてください。





ファイルの移動



これで準備が完了です。CordovaプロジェクトとMonacaプロジェクトの互換性が向上していることでこのような流用が可能になっています。



ただし、Monacaプロジェクト上にこれらのファイルがあるのは良くないので、MonacaプロジェクトをGitに登録してこれをcloneし、そこにCordovaプロジェクトのファイルを入れつつ.gitignoreで元のMonacaプロジェクトには影響しないようにしておくのが良いかと思います。



Appiumのインストール



AppiumはiOS9への対応が進められているベータ版を使っています。





$ npm install appium@beta -g


Xcode7にするとインストール時に library not found for -lgcc_s.10.5 というエラーが発生する可能性があります。その時には下記コマンドを実行することで回避することができます(自己責任でお願いします)。





$ cd /usr/local/lib  & & sudo ln -s ../../lib/libSystem.B.dylib libgcc_s.10.5.dylib


Rubyライブラリのインストール



今回はRubyでテストコードを書いてみます。まずRubygemsでライブラリをインストールします。





$ gem install --no-rdoc --no-ri appium_lib


これで準備は完了です。



アプリをビルドする



テストを行うにあたって一旦ビルドを行う必要があります。Monacaアプリのディレクトリに移動して実行します。





$ monaca build android


これでアプリがビルドされればOKです。



デモコードを書いてみる



まずはテストまでいかず、簡易的に動かしてみたいと思います。





require 'appium_lib'

capabilities = {
   app: "/path_to/platforms/android/build/outputs/apk/android-debug.apk",
   platformName: "Android",
   deviceName: "Android",
   defaultCommandTimeout:7200,
   debugLogSpacing: true
}

@wd = Appium::Driver.new(caps: capabilities[:android])
Appium.promote_appium_methods Object
@wd.start_driver
sleep(5)
@wd.set_context("WEBVIEW_com.example.helloworld")
h1 = @wd.find_elements(:xpath, '//h1')[0]
puts h1.text
a = @wd.find_elements(:xpath, '//a')[0]
a.click
sleep(20)
driver_quit


appオプションは先ほどビルドしてできたアプリのパスを指定してください。driver_quitを実行するとすぐに終了してしまうので20秒のスリープを入れています。clickのようなイベントも使えますのでアプリの画面を遷移しつつ操作をDOMの状態をチェックすると言ったこともできます。



後はこのコードを実行すればOKです。以下はデモで動かしたところですが、実際にはもっと時間がかかります。







注意点



エラーについて



Androidではセッションがたびたび切れることがあります。そうした時には @wd.start_driver を再度実行してください。



iOSとの違いについて



AndroidではXPathを使ったオブジェクトの取得になります。また、コンテキストをWEBVIEW_〜ではじまるものにセットする必要があります。そのため似たような処理をテストする場合においてもiOS/Android版で共通のテストにはしづらそうです。



実機でのテストについて



Androidエミュレータなので実機を接続してもさほど変わらずテストが行えるはずです(未検証)。エミュレータIntel版にしても速度が遅くなってしまうので実機テストの方が良いかもしれません。






ハイブリッドアプリのUIテストについては情報が多くなく、まだまだ手探りのところが多いのですが検証しつつ情報を出せればと思います。良いTipsがあればぜひ共有してください!

MonacaアプリのUIテストに。Appiumを試す

アプリのテストを行う方法は幾つかあります。まず機能レベルでのテストを行うユニットテストがあります。また、サーバを使っている場合は負荷テストを行うこともあるでしょう。しかしこれらのテストだけでは分からない、実際の操作を通じて意図した通りに動いているか確認するUIテストがあります。アプリにおいてはUI/UXが大事なので特に重要なテストです。



Webの世界ではUIテストを行う仕組みとしてSeleniumがよく知られています。それをiOS/Androidアプリに対して適用したのがAppiumです。





Appiumのサイト



今回はこのAppiumをMonacaアプリで動かす方法について紹介します。



CLIMonacaの準備



Appiumはローカルで動かしますのでMonaca.ioではなくCLI版のMonacaを使います。そのためnode.js/npmはインストールされているものとします。





$ npm install monaca -g


インストールしたらログインを行います。





$ monaca login


ログインしたら新しいプロジェクトを作成してみましょう。今回はHelloWorldアプリにしました。





$ monaca create Hello
Which project template do you want to use?

1: Hello World App
2: Minimum Template
  :
21: NIFTY Cloud mobile backend
22: Onsen UI Minimum Template for Universal App

Type number>1
Project created successfully.


Cordovaコマンドのインストール



次にCordovaコマンドをインストールして、適当なプロジェクトを作成します。





$ npm install cordova -g
$ cordova create HelloCordova
$ cd HelloCordova


今回はiOSをプラットフォームとして追加します。





$ cordova platform add ios
Adding ios project...
iOS project created with cordova-ios@3.9.1
Discovered plugin "cordova-plugin-whitelist" in config.xml. Installing to the project
Fetching plugin "cordova-plugin-whitelist@1" via npm
Installing "cordova-plugin-whitelist" for ios


そうするとこのようなファイル構成になります。





Cordovaプロジェクトのファイル構成



platforms/ios 以下のファイルを Monacaプロジェクトの platforms/ios 以下に移動します。





ファイルの移動



これで準備が完了です。CordovaプロジェクトとMonacaプロジェクトの互換性が向上していることでこのような流用が可能になっています。



ただし、Monacaプロジェクト上にこれらのファイルがあるのは良くないので、MonacaプロジェクトをGitに登録してこれをcloneし、そこにCordovaプロジェクトのファイルを入れつつ.gitignoreで元のMonacaプロジェクトには影響しないようにしておくのが良いかと思います。



Appiumのインストール



AppiumはiOS9への対応が進められているベータ版を使っています。





$ npm install appium@beta -g


Xcode7にするとインストール時に library not found for -lgcc_s.10.5 というエラーが発生する可能性があります。その時には下記コマンドを実行することで回避することができます(自己責任でお願いします)。





$ cd /usr/local/lib  & & sudo ln -s ../../lib/libSystem.B.dylib libgcc_s.10.5.dylib


Rubyライブラリのインストール



今回はRubyでテストコードを書いてみます。まずRubygemsでライブラリをインストールします。





$ gem install --no-rdoc --no-ri appium_lib


これで準備は完了です。



アプリをビルドする



テストを行うにあたって一旦ビルドを行う必要があります。Monacaアプリのディレクトリに移動して実行します。





$ monaca run ios


これでアプリが起動すればOKです。





アプリの起動



デモコードを書いてみる



まずはテストまでいかず、簡易的に動かしてみたいと思います。





require 'appium_lib'

capabilities = {
   app: "/path_to_app/TestApp.app",
   platformName: "iOS",
   deviceName: "iPhone Simulator",
   showIOSLog: true,
   defaultCommandTimeout:7200,
   debugLogSpacing: true
}

@wd = Appium::Driver.new(caps: capabilities)
Appium.promote_appium_methods Object
@wd.start_driver
begin
  @wd.texts
rescue
  @wd.start_driver
end
puts @wd.texts[0].text # HelloWorld!
@wd.texts[1].click
sleep(20)
driver_quit


appオプションは先ほどビルドしてできたアプリのパスを指定してください。driver_quitを実行するとすぐに終了してしまうので20秒のスリープを入れています。clickのようなイベントも使えますのでアプリの画面を遷移しつつ操作をDOMの状態をチェックすると言ったこともできます。



後はこのコードを実行すればOKです。以下はデモで動かしたところですが、実際にはもっと時間がかかります。







注意点



エラーについて



@wd.texts を実行したタイミングで





underlying webdriver instance does not support javascript (Selenium::WebDriver::Error::UnsupportedOperationError)


というエラーを起こす こと があります。その場合は再度実行してあげると直るようです。



ipaファイルでのテストについて



Appiumはipaファイルを使った実機テストにも対応しているのですが、うまくいきませんでした。そこでCordovaプロジェクトのスケルトンをベースにappを使ってシミュレータでテストを行っています。そのため今回の方法では実機では動きませんのでご注意ください。






ハイブリッドアプリのUIテストについては情報が多くなく、まだまだ手探りのところが多いのですが検証しつつ情報を出せればと思います。良いTipsがあればぜひ共有してください!