LoginSignup
28
29

More than 5 years have passed since last update.

Groovy+Spock for Android app testing

Posted at

TL; DR

  • AndroidアプリのテストにGroovy + Spockを利用するとたのしいよ
    • power-assertっぽいメッセージが出るよ
    • ParameterizedTestしやすいよ
    • 標準添付のMockが高機能だよ
  • Local unit testではSpockがそのまま使えるよ
  • Instrumented unit testではspock-androidが便利だよ

Introduction

Wantedly Advent Calendar 2015 2日目です.
Wanteldyでは主にRails・JS(AngularJS)・CSSあたりを触ってた@izumin5210がお送りします.
メインはサーバサイド・Webフロントエンドですが,今回はAndroidのテストのお話をします.
好きなテスティングフレームワークはRSpec,Mocha等のゆるふわspecです.よろしくお願いします.

本記事はpotatotips #22にて筆者が話をした『Groovy/Spock for Android app testing』及び『GroovyとSpockでテスト書く話してきた #potatotips - うさみみを生やせ』を補完する内容になります.

Spock

SpockはGroovy製のJava/Groovy向けテスティングフレームワークです.超ざっくりとした特徴だけあげると以下のような感じです:

  • RSpecとかに影響受けてるらしい
  • power-assertっぽい出力
    • むしろpower-assertがSpockの影響を受けてる
  • めっちゃParameterized Testしやすい
  • いいかんじのMock/Stub/Spyを標準搭載

Spockの魅力については『JavaのユニットテストにSpockを適用する - Qiita』という記事を読んでいただくか,Spock Web Consoleで遊んでもらうと手っ取り早くわかっていただけるような気がします.
先の記事で紹介されてない特徴としてMockまわりのが高機能かつAPIが超柔軟というのがありますが,それはまたの機会に…(参考: MockStubSpy).

個人的には,メソッド呼び出しの順番の検証とかをwhen - thenを交互に書き連ねてすごいバカっぽく書けるところが好きです(褒めてる).意味不明なぐらい柔軟なMockまわりのAPIとかParameterized Testしやすいのもお気に入りポイントです(めっちゃ褒めてる).

さて,次節からはAndroidアプリ開発におけるテストでSpockを利用する方法について紹介します.Androidのテストを大きく2種類に分けて説明していきます.

Local unit test

Building Local Unit Tests | Android Developers』にあるような,ローカルマシンで回す(= Android frameworkに依存しない)ユニットテストです.Utilsクラスや完全にAndroidから切り離されたビジネスロジックのテストに用いる,src/test以下に書いていくアレです.

Dependencies

普通にSpockをdependenciesに追加します.SpockのMock/Stub/Spy等を利用する場合はcglibも一緒に追加しておきましょう.

dependencies {
    // ...

    testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'
    testCompile 'cglib:cglib-nodep:2.2'
}

Usage

従来のJUnitなテストではsrc/test/java以下にテストコードを記述していましたが,SpockはGroovyのtesting frameworkなのでsrc/test/groovy以下に書いていくことになります.ファイルも*.groovyになります(本場では*Spec.groovyにすることが多いのかな?).

例えばkonashi SDK for Androidでは,『Characteristicから取り出したバイト列を変換する』みたいなSpockお得意のParameterized Test案件等で利用されています.また,拙作のRedux for AndroidなライブラリであるDroiduxにおいては,『middlewareの呼び出し順』や『undo時にHistoryの状態がちゃんと意図通りに遷移するか』といった真面目にテストするのが激しくめんどうくさい箇所で利用しています.

Instrumented unit test

Building Instrumented Unit Tests | Android Developers』にあるような,エミュレータや実機で回す(= Android frameworkに依存する)テストです.ActivityやContextが絡んでくるようなテスト,Android固有のクラスとかは使ってる場合はこっち,src/androidTest以下に書くやつです.

Dependencies

こちらはSpock本体に加えてspock-androidとGroovyが,Mockの類を利用するにはcglibではなくdexmaker 1.2GitHubに移動する前のやつ)が必要です.Espressoを利用したい人はお好みでどうぞ(一応,動いたのを確認しました).

android {
    defaultConfig {
        // ...
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    // ...
    packagingOptions {
        exclude 'META-INF/services/org.codehaus.groovy.transform.ASTTransformation'
        exclude 'LICENSE.txt'
    }
    // ...
}

dependencies {
    // ...

    androidTestCompile 'com.android.support.test:runner:0.4.1'
    androidTestCompile 'com.android.support.test:rules:0.4.1'
    androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.1'

    androidTestCompile 'org.codehaus.groovy:groovy:2.4.4:grooid'
    androidTestCompile('org.spockframework:spock-core:1.0-groovy-2.4') {
        exclude group: 'org.codehaus.groovy'
        exclude group: 'junit'
    }
    androidTestCompile 'com.andrewreitz:spock-android:1.2.2'
    androidTestCompile 'com.crittercism.dexmaker:dexmaker:1.4'
}

GitHub Repository名がandroid-spock,artifact名がspock-androidです.ややこしいわ.

Usage

こちらも従来のsrc/androidTest/javaではなくsrc/androidTest/groovy以下の*Spec.groovyにテストコードを書いていくことになります.こちらに関しては自分で書いたpublicなコードがないので,公式に添付されてるサンプルコードをどうぞ.

// https://github.com/pieces029/android-spock/blob/master/spock-android-sample/src/androidTest/groovy/com/example/spock/MainActivitySpec.groovy
class MainActivitySpec extends Specification {

  @UseActivity(MainActivity)
  def activity

  def "test activity setup"() {
    expect:
    activity != null
    activity instanceof MainActivity
  }

  def "test layout"() {
    given:
    def button = activity.findViewById(R.id.main_button) as Button

    when:
    def buttonText = button.getText()

    then:
    buttonText == "Test"
  }
}

@UseActivityのようなアノテーションを付けることで,SpockのExcensionという仕組みによりActivityが"注入"されます(この表現が正しいかはわからない).Mockを利用したいときはスーパークラスをAndroidSpecificationに取り替えれば使えるようになります.

最近のテスト環境では@Rule ActivityTestRule rule;みたいな感じのを使うことでActivity起動のタイミングを制御できるっぽいですが,spock-androidだとちょっと厳しそうです.4日前ぐらいに出たspock-android 1.2.2で「SpecificationごとにActivity起動」「MethodごとにActivity起動」みたいなオプションが付いたみたいです,が,ActivityTestRuleほどの自由度はないような気がしています.

Robospock?

AndroidでSpockというとRobospockを思い浮かべる方もいると思います.RobospockはJVM上で動かすやつなので,どちらかというとRobolectricに近いのかもしれません(実はよく知らない).今回は取り扱いません.

:warning: ハマりどころ :warning:

groovy-gradle-android-plugin問題

Groovy及びSpockをAndroidアプリ開発で利用する際にはgroovy-gradle-android-pluginを利用します.このpluginですが,開発が停滞気味なのかAndroid Studio(正確にはGradle向けAndroid Pluging)のアップデートによりなんか01んだりします(関連issue: #53 #61).大体はわりとすぐに対応されますが,pluginの自体のバージョンアップはなかなかこないので気をつけましょう.最近ではSNAPSHOTSすら更新されてないとかいうおもしろ事態になってますが,そんなときはjitpack.ioから最新のmasterを取ってくればだいたいなんとかなります(関連issue: #68).12/2時点でAndroid Plugin 1.5とGroovy Plugin最新のmaster(1b77dd6763)の組み合わせでの動作を確認できています.

buildscript {
    repositories {
        jcenter()
        maven { url "https://jitpack.io" }
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:1.5.0'
        classpath 'com.github.groovy:groovy-android-gradle-plugin:1b77dd6763'
    }
}

apply plugin: 'com.android.application'  // or 'com.android.library'
apply plugin: 'groovyx.grooid.groovy-android'

Test Artifact問題

Android Studioを利用する場合,Build VariantsからTest Artifactを適切に設定しないと正しくテストが実行されません(CLIからだとだいじょうぶ).
image.png (19.0 kB)

Groovyのバージョン問題

org.spockframework:spock-core:1.0-groovy-2.4みたいなのをdependenciesに追加したと思いますが,このGroovyのバージョンを合わせないとテストが落ちます(androidTestのビルドで落ちる).gradle-wapperを利用している場合,gradleのバージョンが古すぎるとGroovyの2.3とか入ってたりするので,<PROJECT_ROOT>/grade/wrapper/gradle-wrapper.propertiesにあるdistributionUrlを適切に設定しましょう(2.9ならとりあえず問題ないはず).

distributionUrl=https\://services.gradle.org/distributions/gradle-2.9-all.zip

もしくはAndroid StudioのProject StructureからGradleのバージョンを弄れば大丈夫です.

image.png (18.8 kB)

動かないときは

とりあえずcleanするか,invalidate cache and restartしてみましょう.コード変換とかしている影響か,ライブラリ入れたり出したり試行錯誤している間はとくになんかおかしくなりやすいです(?)

まとめ

既存プロジェクトのテストをすべて置き換えていくとかだとしんどいですが,たとえば導入の障壁が低いLocal unit testをSpockにしていくとかからはじめていくと楽かもしれません.いずれのテストも(クラス名が被ってなければ)JUnitとの共存も可能なので,チームメンバーの同意が得られるならばじわじわと布教していくのもいいですね(?)
spock-android自体はまだまだ元気ですが,Android-Groovy界隈の動きが若干鈍いのが気になるかもしれません.

References

28
29
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
28
29