やらなイカ?

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

第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更新しました

参考資料

Gradle+AndroidプラグインでNDKプロジェクトをビルドする

Androidの新ビルドシステムであるGradle plugin for Androidの導入と、標準でまだNDKに対応していないので、その対応方法をまとめました。

【2014/6/11追記】Gradle plugin for Android 0.7でNDKがサポートされました。こちらの記事を参照してください Gradle+Androidプラグイン(0.7以降)でNDKプロジェクトをビルドする - やらなイカ?

尚、今回は(新ビルドシステムの売りである)Product Flavorには対応していません。ディレクトリ構成変えなければいけないので、改めて。

環境

  • ADT 22.0.0(任意/build.gradle生成に使用)
  • Android SDK Platform-tools 17(17以上必須)
  • Gradle 1.6(これからインストール)
  • Gradle plugin for Android 0.4(Gradleによってインストール)

GVM(Groovy enVironment Manager)のインストール

$ curl -s get.gvmtool.net | bash

.bash_profileにパスが追加されているので、Terminalを開きなおす

Gradle 1.6 をインストール

$ gvm list gradle
$ gvm install gradle 1.6
(snip)
Done installing!
Do you want gradle 1.6 to be set as default? (Y/n): y

ちなみにデフォルトバージョンの切り替えは、

$ gvm default gradle 1.6

でできます。

build.gradleをつくる

ADT 22に、Gradleのビルドファイル生成機能が付いたので利用します。

  1. Eclipseのメニューから [File] - [Export...] を選択
  2. Android → "Generate Gradle build files" を選択して [Next >]
  3. 対象プロジェクトを選択して [Finish]
  4. プロジェクト直下にbuild.gradleができます

このままGradleでビルドしようとすると、

FAILURE: Build failed with an exception.
(snip)
* What went wrong:
A problem occurred evaluating root project 'GradleAndroidNdkExample'.
> SDK location not found. Define location with sdk.dir in the local.properties file or with an ANDROID_HOME environment variable.

と言われるので、今回はlocal.propertiesにSDKのパスを設定しました。 local.propertiesは、android update projectコマンドでも生成されるので、既にAntでビルドしているプロジェクトなら不要なはず。

NDKに対応する

共有ライブラリをapkに取り込む

そのままgradle buildを実行しても、NDKのビルドはおろか、libs/下の共有ライブラリもapkにバンドルされませんので、build.gradleに以下を追加します

tasks.withType(com.android.build.gradle.tasks.PackageApplication) {
    pkgTask -> pkgTask.jniDir new File(projectDir, 'libs')
}

これにより、apkにlibs/下の(ndk-build済みの)共有ライブラリが組み込まれるようになります。

ndk-buildも実行させる

プロダクト・フレーバーごとに共有ライブラリもビルドしなければならない場合、コンパイルごとにndk-buildを実行させるため、build.gradleに以下を追加します。

task ndkBuild(type:Exec) {
    commandLine 'ndk-build'
}
tasks.withType(Compile) {
    compileTask -> compileTask.dependsOn ndkBuild
}

ただ、ndk-buildが失敗しても後続タスクは止まらない気がするのでご利用は計画的に。

Gradleでビルドする

$ cd プロジェクトのパス
$ gradle build

Antの場合、apkはbin/に生成されますが、Gradleではbuild/apk/に生成されました。

サブコマンドbuildは、デフォルトでビルドタイプdebugとrelease両方のビルドを行ないます。まだ署名の設定を書いていないので、releaseはunsigned.apkまで生成されています。

Relaseビルドに署名を追加する

build.gradleに署名の定義を追加します*1

【5/23追記】キーストア情報をbuild.gradleではなく外部ファイル(gradle.properties)に記述できました。 Gradle+Androidプラグインで証明書情報をgradle.propertiesに移す - やらなイカ? を参照。

android {} 内に以下を追加します。

signingConfigs {
    myConfig {
        storeFile file("example.keystore")
        storePassword "example"
        keyAlias "example"
        keyPassword "example"
    }
}

buildTypes {
    release {
        signingConfig signingConfigs.myConfig
    }
}

再びビルド。今回はreleaseビルドのみ実行します。

$ gradle assembleRelease

今度はrelease-unaligned.apk(zipAlign前)と、release.apkが生成されました。

サンプルプロジェクト

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

尚、

  • JNIラッパーの生成にはSWIG/Javaを使っています。.iファイルもjni/下に置いてあります。
  • keystoreはリポジトリに含めていません

参考資料

*1:build.gradleに直接書かなければいけないのが非常に気持ち悪いですが!