やらなイカ?

たぶん、iOS/Androidアプリの開発・テスト関係。

システムテスト自動化カンファレンス2013で「スマートフォンアプリのテスト自動化をはじめよう」をお話してきました #stac2013

テスト自動化研究会主催のシステムテスト自動化カンファレンス2013にスタッフとして参加&モバイル枠をいただいてお話してきました。

システムテスト自動化カンファレンス2013ツイートまとめ - Togetterまとめ

毒食わば皿まで

古来より「毒食わば皿まで」という言葉がありまして、これはつまり「スライドを使いまわした*1ならブログエントリも使い回せばいいじゃない」という意味なのですが、さすがに心苦しいので以下オリジナルの補足をします。

尚、スライド自体もiOSに関する記述を追記したり*2、構成を見なおしたりしています。

テストレベルについての補足

途中で言った「『ユニットテストの話はするな』という圧力」はもちろん冗談なのですが、テストレベルに関して説明不足を感じたので補足します。

スライドでは「ユニットテスト」で済ませている部分、デブサミ2012でのAndroidテスト部講演や、書籍Androidアプリ テスト技法では「モデル*3ユニットテスト」を適切に切り出してテストすべきで、テスト困難なViewとControllerはシステムテストなど上位で実施すればいい*4のではないか、という方針でした。

それに対し、現時点では「モデルだけでなく、ビューのユニットテスト」まで行なった上で、Controllerはシステムテストで担保できれば良いのではないか、と考えています。

スライドにも「(システムテストでは)日時、天気、株価、為替、乱数などに起因するJudge*5を無理にはしない」と書いていますが、表示内容や最小桁/最大桁での見栄えはユニットテストで実施可能です。

もちろんUIの変更が発生するとテストのメンテナンスも必要になってしまうのですが、システムテストレベルで(スタブサーバを用意するなどして*6)実施するテストよりも楽に対応できるはずです。

尚、Viewのユニットテストを行なう前提は以下の二点です。

  • GHUnitなどイメージ比較をサポートしているテストフレームワークを使う
  • Viewに表示内容を渡す部分が切りだされている(MVVMにおけるViewModel的なもの。ControllerにsetText()等が書かれていないこと)

まずユニットテストからはじめなければ!という方は、テスト技法からユニットテストの基礎、Mockito、Robotiumなども載っているこちらの書籍をぜひお読みください(宣伝)

Androidアプリ テスト技法

Androidアプリ テスト技法

質疑応答(12/6追記)

これからモバイルのテストをやろうとする人へのオススメは?

まず、ちゃんとユニットテストを書きましょう。 開発者テストから入れない事情があるのであれば、Monkey Talkなどでハッピーパスから入ることをおすすめします。

モバイル向けWebアプリとネオティブアプリとの違いは?

システムテストなど上層のテストでは観点や手段などはほとんど変わらないと思います。ユニットテストなど開発者テストでは差があるはず。 WebViewのテストはMonkey Talkでもサポートされています。

システムテストではスクリプトを書くスキルは必要なのか?

自分は(開発者なので)ツールでキャプチャしたものより、スクリプトを書いています。 ひとつのアプリを複数の環境にカスタマイズしている場合など*7、キャプチャしたものをコピペしたものではメンテナンスが大変。

まとめ・感想

まずスタッフとして、手応えのある、とても良いイベントだったと思っています。講演すべて聞けたわけではないのですがどれもとても濃い内容でしたし*8、なにより会場の反応が良く、楽しんでいただけている雰囲気、一体感のようなものを感じられました。

テスト自動化研究会で初の主催イベント、しかもキャパ200人。研究会メンバーで運営にあたりましたが、事前準備、段取り、また会場をご提供いただいた日本オラクル様のサポート、また何より来場いただいた方々のご協力があって大きなトラブルもなく終えることができたと思っています。

自分の発表に関しては、Tizenとか自動化ハイ(©TABOK関西)とかがウケてよかったです。ハイと言えばDIO様のあれをネタ画像に使おうかとも思ったのですが自重したのが少し心残りです。

WebViewのテストについて

Seleniumハンズオン参加者の方から質問をいただいたそうなので(又聞き)ここで回答します。

アプリ内に組み込んだUIWebView/WebViewのテストは、少なくともMonkey Talkではサポートされています。

またSelenium自体にもAndroid Driverがバンドルされ、iOS向けにはios-driverというフレームワークも公開されているようなので、そちらも検討してみてはいかがでしょうか。WEB+DB PRESS Vol.77の特集1-3で紹介されていました。

WEB+DB PRESS Vol.77

WEB+DB PRESS Vol.77

  • 作者: 中川勝樹,山内沙瑛,舟崎健治,吉荒祐一,今井雄太,八木橋徹平,安川健太,近藤宇智朗,奥野幹也,天野祐介,賈成カイ,伊藤直也,住川裕岳,北川貴久,菅原一志,後藤秀宣,久森達郎,登尾徳誠,渡邊恵太,中島聡,A-Listers,小俣裕一,はまちや2,川添貴生,石本光司,舘野祐一,沖田邦夫,澤村正樹,卜部昌平,吉藤博記,片山暁雄,平山毅,WEB+DB PRESS編集部
  • 出版社/メーカー: 技術評論社
  • 発売日: 2013/10/24
  • メディア: 大型本
  • この商品を含むブログ (3件) を見る

12/3追記

@dageziさんより、RobotiumでもWebViewの中のエレメントにアクセスできるとの情報をいただきました。ありがとうございました!

*1:ひとつ前のエントリ参照。でも結構アドリブで違うこと喋ってるのです

*2:そもそもこちらがオリジナルで、Quesは後から決まった話なので削った、が正しい

*3:MVCのModel

*4:Controllerの責務にフォーカスするという意味ではなく、画面遷移やUIイベントを実行すれば確認できたと言えるので

*5:自動化の三要素(Drive, Judge, Report)からこう書いていますが、xUnitで言うAssertion

*6:そもそもスタブサーバを使うテストがシステムテストと言えるか、という話もあります

*7:これはちょっと例が悪かったのですが

*8:私のが一番内容薄くてごめんなさい感でいっぱいです

第3回Quesで「ここからはじめる!Androidアプリのテスト自動化」をお話してきました

QuesというQAエンジニアイベントで、Androidアプリのシステムテスト(システムレベルの機能テスト、回帰テスト、UIテスト、等々)自動化についてお話してきました。

f:id:nowsprinting:20131115192940j:plain

"イケてるQAエンジニアの集い"と銘打たれたこのイベント、QAエンジニアの現場の横のつながり、ナレッジの共有、交換などを目的に半年周期で開催されているとのこと。

今回は自動化がテーマということで、前半は@snskさんの「テスト自動化術概論アルファ版」、後半に「ここからはじめる!Androidアプリのテスト自動化」と題して、(QAさん向けなので)上層のテストを自動化する第一歩、取っ掛かり的なお話をさせていただきました。

ユニットテストについての補足

ユニットテスト/デベロッパーテストについてはスライド1枚で流してしまったので、情報源だけ補足します*1

Androidテスト部による@IT連載記事

書籍

Androidアプリテスト技法

Androidアプリテスト技法

Androidテスト部本(共著)。テスト設計など基礎と、主にデベロッパーテストの手法。Robotiumも紹介しています(担当は私)

入門 Androidアプリケーションテスト

入門 Androidアプリケーションテスト

ブリリアントサービスの瀬戸さんの著書

JUnit実践入門 ~体系的に学ぶユニットテストの技法 (WEB+DB PRESS plus)

JUnit実践入門 ~体系的に学ぶユニットテストの技法 (WEB+DB PRESS plus)

JUnitそのものの理解にはこれが最適です。Androidのテストに関してもページを割かれています

質疑応答

質疑応答や懇親会でいただいた内容を、覚えている範囲で。

結局、おすすめのフレームワークは何ですか?

慣れ+JUnitのコードを書くのに抵抗がないという前提で、Robotium。スクリプトを書くハードルが高いチームならば、MonkeyTalk。

機種依存問題にはどう取り組むべきか

まず、解像度・アスペクト比から来る見栄えの問題と、OS/機種固有のバグ的な問題は分けて考える。見栄えの問題は(今のところ)全画面を通すスクリプトでスクリーンショットを撮り、それを評価する。

OS/機種固有の問題に対しては、mixiさんが公開しているオープンソースのライブラリを使うなど、開発側でピンポイントに対処するべき。

see: mixi-inc/Android-Device-Compatibility · GitHub

アプリにAgentを組み込むツールは、性能等に問題が出ないか

性能、セキュリティなどに問題があるので、リリースするビルドには組み込むべきではない。テスト用のビルドであっても、パフォーマンステストなどで気になるようであれば、Agentを必要としないツールの利用を検討してもいいかも。

リリース用、テスト用など個々のビルドを作るにはGradle+Androidプラグイン導入がおすすめ。

参考:

まとめ・感想

圧倒的アウェイ感に加え、実際に現状Androidアプリに関わっている方は数名*2iOSはもう少しいる、という程度だったので正直どこまでお役に立てたのか?ではありますが、懇親会で実際に自動化に取り組んでいる方、取り組もうとしている方とお話できたので良しとしたい。

あと、スライドに仕込んだネタのウケ方が足りなかったので、これからも精進しようと思いました。

*1:宣伝ともいう

*2:挙手ベース調べ

Genymotionがとっても早いAndroidエミュレータらしいので試してみた

実機より早くてテストがはかどると話題の Genymotion(旧称 AndroVM)を試してみたのでメモ。 環境は Mac OS X 10.8.4

Genymotion「私には誰も追いつけないよ!」

Genymotionのインストール

  1. Genymotionにサインアップして Mac OS X 64 bits版をダウンロード。執筆時点のバージョンはv1.2.1
  2. ダウンロードした.dmgファイルをマウントしてGenymotion.appを/Applicationディレクトリにコピー
  3. VirtualBoxがインストールされていないと動かないので、インストールしていない場合は Downloads – Oracle VM VirtualBox からダウンロード、インストール(執筆時点のバージョンは4.2.18)
  4. Android SDKも必要なので、 Developer Tools | Android Developers からダウンロード、インストール

Genymotion.appを起動

  1. はじめは仮想マシンが無い状態なので、"Add"で Genymotion Cloud から定義を選択して"Add"で取り込みます
  2. "Menu" -> "Settings"の順にクリック、Android SDKのパスを設定する
  3. "Your virtual devices"にエントリされたものを選択して"Play"をクリックすると、仮想マシンが起動します(この時点で速さを実感できるはず!)

f:id:nowsprinting:20130907170805p:plain

ウィンドウの右側にあるアイコンで、端末の回転、電源ボタン、音量、バッテリー、GPSのエミュレートも可能。("Power off"はずっと待ってても仮想マシン終ってくれないようなので、cmd+Qで落としました)

adbコマンドも使用できます

$ adb devices
List of devices attached
192.168.56.101:5555 device

adb installで手元のapkをインストール、実行もできました。

IntelliJ IDEAプラグインのインストールと設定

プラグインはほぼダウンロードページに書かれている手順で、Android Studioでも使用できました。以下の手順でインストールできます

  1. Android Studioを起動
  2. メニュー -> [Preferences...]
  3. "IDE Settings"の下のほうにある"Plugins"行を選択
  4. [Browse repositories...]をクリック
  5. 検索フィールドで"genymotion"を検索し、ヒットした行をダブルクリックしてインストール(執筆時点で v1.0.1)
  6. Preferencesウィンドウを閉じようとするとAndroid Studioの再起動を促されるので、再起動
  7. 再びPreferences。"Genymotion"行が追加されているので選択し、インストール済みの Genymotion.app のパスを設定

続いて、起動していきます。

1.Android Studioのツールーバーに"Genymotion Device Manager"が追加されているのでクリック

f:id:nowsprinting:20130907171509p:plain

2.Genymotion.appで定義済みの仮想マシンがリストされるので、ひとつ選択して[Start...]で仮想マシンが起動します

f:id:nowsprinting:20130907171720p:plain

3.Android Studioの"Monitor"からも起動した仮想マシンが見えていますし、"Run"でもapkをインストール/実行するターゲットの選択肢にGenymotion仮想マシンが出てきます

f:id:nowsprinting:20130907171800p:plain

NDKを使ったアプリの起動

Android NDKを使用し、ARM向けにビルドしたapkはそのままでは起動しませんでした。手元のプロジェクトでは、Application.mkのAPP_ABIに"x86"を追加してビルドしなおすことで、インストール、起動まで確認できました。

公式にARM互換的なことが書いてあったのですが、どうなんでしょう?

参考

Android 4.2のCanvas#drawRect()が描画してくれないケース

android.graphics.RectFのClass Overviewには、

Note: most methods do not check to see that the coordinates are sorted correctly (i.e. left <= right and top <= bottom).

と書かれていますが、Android 4.2のCanvas#drawRect()はまさにこれに該当し、例えば left > right の場合、矩形を描画してくれません。

確認したのは下記機種です。

  • Galaxy Nexusの4.1.1では left > right でも描画され、4.2.2では描画されませんでした
  • Nexus 7(4.2)でも描画されませんでした

こんな気持ち悪いコードは書かないと思うのですが。*1

こんなケースがあると、単純に画面巡回してスクリーンショットを単純に画像比較する形の自動テストもあって損はない気がします。*2

*1:今回は某国某社から引き継いだソースでやられました

*2:そんな気はしていつつ、ありませんでした。今回の発見は目視。

Gradle+AndroidプラグインでProguardを有効化する

Gradle plugin for Android 0.4ではProguardも使えるようなので、試してみました。

Gradleの実行環境は前記事 Gradle+AndroidプラグインでNDKプロジェクトをビルドする - やらなイカ? 参照。

build.gradleの設定

buildTypesに以下を追加します。

buildTypes {
    release {
        (snip)
        runProguard true
        proguardFile getDefaultProguardFile('proguard-android.txt')
        proguardFile file('proguard-project.txt')
    }
}

proguard-android.txtは、${sdk.dir}/tools/proguard/proguard-android.txtにあるはずです。

proguard-project.txtはプロジェクト固有のproguard設定で、プロジェクト直下に置きます。

ビルド

$ gradle assembleRelease

を実行するとproguardが適用されたapkが生成されました。

また、マッピングファイルは build/proguard/release/mapping.txt に出力されています。

サンプルプロジェクト

https://github.com/nowsprinting/GradleAndroidNdkExample更新しました

参考資料

Gradle+Androidプラグインでプロダクトフレーバーを試してみた

Androidの新ビルドシステム(Gradle + Gradle plugin for Android)では、プロダクト・フレーバーという仕組みによってひとつのプロジェクトから複数の(異なるPackageNameを持つ)apkをビルドできます。

これによって、以下のようなものを容易に提供することができるようになります。

  • 画像リソースなどの異なる、同じ仕組みのアプリ
  • 無料版と有料版
  • ステージングサーバに接続するといった、開発中に使用するアプリ
  • 異なるSDkバージョン、異なるアーキテクチャに最適化したアプリ

Gradleの実行環境は前記事 Gradle+AndroidプラグインでNDKプロジェクトをビルドする - やらなイカ? 参照。これに加え今回は Android Studio 0.1 をプロジェクトのひな形作りに使用します。

新規プロジェクトの作成

プロダクト・フレーバーを使用するには、従来のEclipseのプロジェクト構成ではなく、Gradleの流儀に従う必要があります。

新しい開発環境であるAndroid Studioで新規プロジェクトを作成すると、Gradle向きの構成で生成されます。 既存プロジェクトの場合は、これに合わせてディレクトリ階層を変更するなどする必要があります。

GradleAndroidFlavorExampleのディレクトリ構成(新規作成時点)

  • GradleAndroidFlavorExample/
    • build/
    • build.gradle
    • libs/
    • src/
      • main/ (Eclipseにおけるプロジェクトホーム)
        • AndroidManifest.xml
        • ic_launcher-web.png
        • java/ (Eclipseにおけるsrc/ディレクトリ)
        • res/
  • build.gradle
  • gradle/
    • wrapper/
      • gradle-wrapper.jar
      • gradle-wrapper.properties
  • gradlew
  • gradlew.bat
  • local.properties
  • settings.gradle

IntelliJ IDEAのプロジェクトファイルおよび、リポジトリ管理外のファイルは除いています。

ライブラリプロジェクトを追加

フレーバーに先立ち、ライブラリプロジェクトを追加してみます。ライブラリプロジェクトは、プロジェクト直下にlibraries/ディレクトリを作り、その下に配置します。 今回は BoD/android-switch-backport · GitHub を置いてみます。

  • GradleAndroidFlavorExample/
  • build.gradle
  • gradle/
  • gradlew
  • gradlew.bat
  • libraries/
    • android-switch-backport/
      • AndroidManifest.xml
      • res/
      • src/
  • local.properties
  • settings.gradle

ライブラリプロジェクト自体のbuild.gradleは以下のように定義します。

buildscript {
    repositories {
        mavenCentral()
    }

    dependencies {
        classpath 'com.android.tools.build:gradle:0.4'
    }
}

apply plugin: 'android-library'

android {
    compileSdkVersion 17
    buildToolsVersion "17.0.0"

    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            resources.srcDirs = ['src']
            aidl.srcDirs = ['src']
            renderscript.srcDirs = ['src']
            res.srcDirs = ['res']
            assets.srcDirs = ['assets']
        }
    }
}

導入したライブラリプロジェクトのディレクトリ構成が異なるので、sourceSets{}の記述が必要です。

尚、Gradle Plugin User Guide - Android Tools Project Site にはbuildToolsVersionが書かれていませんが、これも必要です。 アプリケーションプロジェクトと同じバージョンを指定する必要があるようです。*1

続いて、追加したライブラリプロジェクトの存在をsettings.gradleに追加します。

include ':GradleAndroidFlavorExample', ':libraries:android-switch-backport'

最後に、アプリケーションプロジェクトのbuild.gradleに、このライブラリプロジェクトを追加します。

dependencies {
    compile files('libs/android-support-v4.jar')
    compile project(':libraries:android-switch-backport')   //←コレ
}

【5/15追記】 buildscript {}は、プロジェクト個々にではなく、プロジェクトのルートにあるbuild.gradle(Android Studioで生成すると空のファイル)一箇所に書くだけでいいようです。
commit: 477fa36 参照。

プロダクト・フレーバーを追加

アプリケーションプロジェクトに、ふたつのプロダクト・フレーバーを追加してみます。まず、build.gradleのandroid{}内に以下を追加します。 packageNameなど、AndroidManifest.xmlの内容を上書きすることができます。

productFlavors {
    squid {
        packageName "com.nowsprinting.gradleandroidflavorexample.squid"

    }
    cuttlefish {
        packageName "com.nowsprinting.gradleandroidflavorexample.cuttlefish"

    }
}

続いて、フレーバーごとの差分を作成します。アプリケーションの"main"と同列に、build.gradleに定義したフレーバー名と同じ名前のディレクトリを作り、その下に差分のファイルを配置します。

今回の例ではイカのようになります。

  • GradleAndroidFlavorExample/
    • src/
      • main/
      • squid/
        • res/
          • values/
      • cuttlefish/
        • res/
          • values/

それぞれのstrings.xmlは、app_nameと表示メッセージを書き換えてあります。画像リソース、Javaソースなど、差分だけ置けば適切に取り込まれます。

尚、今回はAndroidManifest.xmlもmain/のみでフレーバーごとのディレクトリには配置していません。build.gradleのフレーバー定義に書く以上の差分がなければ、main/下のみにひとつあればいいようです。

フレーバーをビルドする

Gradleのサブコマンドassembleにフレーバー名を追加して指定することで、そのフレーバーをビルドできます。

$ gradlew assembleCuttlefish

これで、GradleAndroidFlavorExample/build/apk/下に、

  • GradleAndroidFlavorExample-cuttlefish-debug-unaligned.apk
  • GradleAndroidFlavorExample-cuttlefish-release-unsigned.apk

が生成されます。

さらに、ビルドタイプも指定出来ます。

$ gradlew assembleSquidDebug

この場合、

  • GradleAndroidFlavorExample-squid-debug-unaligned.apk

が生成されます。

$ gradlew build

で、全て(mainも含む)のフレーバー、ビルドタイプがビルドされます。

サンプルプロジェクト

https://github.com/nowsprinting/GradleAndroidFlavorExample に置きました

参考資料

*1:細かくは未検証

Gradle+Androidプラグインで証明書情報をgradle.propertiesに移す

先の記事 Gradle+AndroidプラグインでNDKプロジェクトをビルドする - やらなイカ? で、キーストアの情報(パスワード等)をビルドスクリプトであるbuild.gradleに直接書いていたのですが、gradle.propertiesというファイルに逃がしてやることができました。

gradle.propertiesを作る

キーストアの情報をgradle.propertiesに記述します。キーにピリオドは使えないようなのでant.propertiesとは若干違う書式です。

storeFile=example.keystore
storePassword=example
keyAlias=example
keyPassword=example

build.gradleの変更

build.gradleの、signingConfigsの記述はこのように変更。

signingConfigs {
    myConfig
}

さらに以下を追加。

if (project.hasProperty('storeFile')) {
    android.signingConfigs.myConfig.storeFile = file(storeFile)
}
if (project.hasProperty('storePassword')) {
    android.signingConfigs.myConfig.storePassword = storePassword
}
if (project.hasProperty('keyAlias')) {
    android.signingConfigs.myConfig.keyAlias = keyAlias
}
if (project.hasProperty('keyPassword')) {
    android.signingConfigs.myConfig.keyPassword = keyPassword
}

これでビルドスクリプトとキーストア情報を分離できました。gradle.propertiesを.gitignoreに追加しておけば目的は果たせます。

サンプルプロジェクト

https://github.com/nowsprinting/GradleAndroidNdkExample更新しました

参考資料