Your SlideShare is downloading. ×
  • Like
GroovyなAndroidテスト #atest_hack
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×

Now you can save presentations on your phone or tablet

Available for both IPhone and Android

Text the download link to your phone

Standard text messaging rates apply

GroovyなAndroidテスト #atest_hack

  • 4,007 views
Published

GroovyとSpockを使ってAndroidアプリをBDD (振舞駆動開発)します。Android Ant Runner (http://github.com/taky/android-runner) を使用しています。 …

GroovyとSpockを使ってAndroidアプリをBDD (振舞駆動開発)します。Android Ant Runner (http://github.com/taky/android-runner) を使用しています。
(#atest_hack 発表資料)

Published in Self Improvement
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
4,007
On SlideShare
0
From Embeds
0
Number of Embeds
9

Actions

Shares
Downloads
12
Comments
0
Likes
9

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n

Transcript

  • 1. GroovyなAndroidテスト 8.9.2012 Takahiro Yoshimura (@alterakey)
  • 2. こんな人埼玉で活動しているアーキテクトWebサービスやスマートフォンアプリなどを作っていますAndroidは…まだそこそこ (汗)
  • 3. テスト、間に合ってます?標準添付のAndroid Testing Framework…実機で動かすタイプしかし実機で動かしていると遅い!→ABS、Guavaなどを入れると簡単に数分台のビルド時間→ProGuardなどでShrinkすれば良いが…高速化は本質的に困難
  • 4. そこでGroovyですよ!
  • 5. 背景Groovyって何? JVMで動作する動的言語の一種 Javaと違って型や制約にうるさくない →privateメソッドだろうが何だろうが呼べる →Assertionが親切! →テストに最適 Javaで書かれたライブラリなども利用可能
  • 6. Androidでは?残念ながら、Android開発には簡単に使用できないDiscobotなどの成果もあることにはあるが…しかしJVMで動作させても良いテストには有効
  • 7. …は?JVM?JVMでAndroidなんか動かないだろうが…android.jarを追加してもシステムクラスにはほぼ触れないjava.lang.RuntimeException: Stub!Androidシステムクラスをテストしたいが… … orz
  • 8. Robolectric!AndroidアプリをJVMでテストするためのフレームワークAndroidシステムクラスをことごとく置き換えてテスト可能に ActivityやServiceといったものをそのままnew…! SQLite系ですらエミュレート Fragmentは… Support Packageを使っていればOK
  • 9. だが面倒だGroovyやRobolectricを落としてきて、別途インストールしたりEclipseではJUnitプロジェクトを立てていろいろ設定を…(ryAntではcustom_rules.xmlを作成していろいろルールを…(ry … orz
  • 10. もったいない…ということでAndroid Ant Runnerを作りました→ http://github.com/taky/android-runner (BSD)GroovyやSpockを始めとするテストツールキットをAntベースのプロジェクトから簡単に使えるようにするパッケージ(Android SDK r20以上)Eclipseは?IDEAは?Mavenは?Gradleは? …すみませんが (ryUnix系限定です… ごめんなさい m(_ _)m
  • 11. そもそもなんでテストテスト言っているの?
  • 12. テストは設計手段の一つ詳細仕様を先に定義、コードはそれに合わせる形にクラスの「詳細設計書」としてのユニットテスト振舞駆動開発(BDD)→ プロジェクトに参加しやすく、リファクタリングもしやすく
  • 13. 軽くデモコマンドライン+テキストエディタ導入∼結合テストまでテキストビューアIntentを受け取ってContentResolverに渡してTextViewに表示YouTubeにあります…http://www.youtube.com/watch?v=nrT50tpIjZE
  • 14. では詳細に導入
  • 15. 導入Githubからcloneしてきてbootstrapするだけ! 詳細はREADMEに記載これでGroovy、Spock、Robolectricその他諸々全て手に入る!すぐにでもユニットテストを書ける状態に!
  • 16. Activityの設計MainActivity: メイン画面「This is a text viewer.」と書いてあることまずはテストケースからActivityについては結合テストにも一応書いておく※実機で問題があれば把握したいためここからがほぼGroovy!
  • 17. Activityの設計integration/.../MainActivityTest.java:public class MainActivityTest extends ActivityInstrumentationTestCase2<...> { ... public void test_000() { getActivity(); }}
  • 18. Activityの設計unit/.../MainActivityTest.groovy:import spock.lang.*@RunWith(TestRunner)class MainActivityTest extends Specification { @Test def test_000() { def o = new MainActivity() o.onCreate(null) when: def tv = o.findViewById(R.id.view) then: tv.getText() == This is a text viewer. }}
  • 19. Activityの設計Spockで仕様が読みやすくなる!I. Groovyのみ II. Groovy+Spock// 初期状態 setup: // (省略可)def o = new MainActivity() def o = new MainActivity()o.onCreate(null) o.onCreate(null)// こうした場合 when:def tv = o.findViewById(...) def tv = o.findViewById(...)// こうあるべき then:assertEquals(tv.getText(), ... ) tv.getText() == ...
  • 20. Activityの設計RobolectricはJUnit 4.x、Android JUnitはJUnit 3.x→4.xは @Test Annotationで、3.xは名前でテストを特定@RunWith(TestRunner)→Robolectric標準を多少カスタムしたテストランナー→AARでRobolectricを使うにはこのAnnotationが必要テストランナーは複数指定できない→テストフレームワーク間で取り合いになりやすい orz
  • 21. Activityの設計Robolectricはパラメタライズドテストを扱えないつまり「Spockのexpectを使って似通ったテストをまとめて簡潔に!」 → 今はまだムリ orz単純にRobolectricTestRunnerの問題なので、Robolectricをルールなどの形で再実装できれば解決するかももちろんRobolectricを使わないテストならパラメタライズ可能
  • 22. しくじったよ!実装していないので当然ここから実装を書いて成功させる res/layout/main.xmlのTextViewに: IDを振ってメッセージを入れる
  • 23. 成功だ!次は?ViewActivity: 表示画面Intent.ACTION_VIEWを受けとって中身を表示できること
  • 24. Activityの設計まず表示できること
  • 25. Activityの設計integration/.../ViewActivityTest.java:public class ViewActivityTest extends ActivityInstrumentationTestCase2<...> { ... public void test_000() { getActivity(); }}
  • 26. で、実際の挙動は…??Intent.ACTION_VIEWを受けとって中身を表示できることしかし、どうやってロードしたことを確かめる?Intentはどこからどう飛ばす?テストデータはどう準備すれば?
  • 27. テスト可能性を考えて…ここではTextLoaderとしてロード処理を分離→テスト時はハリボテ(スタブクラス)を噛ませるActivityは単にUIに専念させる→いろいろやっているクラスはテストが難しくなりがち→テストしやすくするためには設計の工夫が必要!IntentはRobolectricで飛ばしたことにできる!→Robolectricのテスト用クラスに直接渡す
  • 28. Activityの設計unit/.../ViewActivityTest.groovy:@RunWith(TestRunner)class ViewActivityTest extends Specification { @Test def test_000() { # TBD: LOAD HERE, with TextLoader! def o = new ViewActivity() o.onCreate(null) when: def tv = o.findViewById(R.id.view) then: tv.getText() == File read! }}
  • 29. Activityの設計unit/.../ViewActivityTest.groovy:@Test def test_000() { ... def o = new ViewActivity() def intent = new Intent(Intent.ACTION_VIEW) intent.setData(Uri.parse( content://test-1 )) Robolectric.shadowOf(o).setIntent(intent) o.onCreate(null) ...
  • 30. スタブはどう作れば…Groovyならスタブの作成も楽々…なはずなのだけどJavaから呼び出されるクラスはGroovyではモックできない!Mockitoを使えばOK→Android Ant Runnerに同梱してある
  • 31. で、スタブはどう作れば…def o = mock(XXX)とした後に…when(o.method1()).thenReturn( foo )when(o.method2(anyObject()).thenReturn( bar )when(o.method2(eq(null))).thenThrow(new NullPointerException())→ o.method1() foo o.method2(Object arg) bar o.method2(null) NullPointerException(例外)
  • 32. で、スタブはどう作れば…簡単なものなら一行で書きたい! …書けますよ?def o = mock(XXX)when(o.method()).thenReturn( foo )def o = when(mock(XXX).method()).thenReturn( foo ).getMock()
  • 33. Activityの設計unit/.../ViewActivityTest.groovy@Test def test_000() { ... # TBD: LOAD HERE, with TextLoader! def loader = when(mock(TextLoader).read()).thenReturn( File read! ).getMock() def loader2 = when(mock(TextLoader).read()).thenReturn( ).getMock() def builder = mock(TextLoader.Builder) when(builder.build(anyObject(), anyObject())).thenReturn(loader) when(builder.build(anyObject(), eq(null))).thenReturn(loader2) def o = new ViewActivity() ...
  • 34. で、スタブはどう渡せば…unit/.../ViewActivityTest.groovydef test_000() { ... def o = new ViewActivity(builder) ... …!!
  • 35. で、スタブはどう渡せば…Dependency Injection (DI)→パラメータなどとしてコラボレータを外部から導入する手法→クラス間の結合度を低下させることができる今回はコンストラクタ(ctor)へ導入→空のctorも作っておく必要があることに注意
  • 36. で、スタブはどう渡せば…Guiceなどを使うとより手軽に適用可能…なはずなのだがそもそもAndroidではInversion of Controlされている環境(i.e. Activityなどはシステムが勝手に作るもの)→これが空のctorを要求される理由いつinjectorを初期化するのかが問題に→RoboGuiceでは専用クラスを継承させている(!!)
  • 37. で、スタブはどう渡せば…Guiceはともかく、RoboGuiceは自動テストの役にはあまり立たなそうな…?
  • 38. 実装テスト失敗、実装へ…TextLoaderは骨組だけ
  • 39. 注意ViewActivityの画面構成についてはテストしていない結合テストで「最低限表示できる」レベルを担保→まだ動かしていないけど…
  • 40. 次は?TextLoader: テキストローダ.read() → あらかじめ指定されたURIの内容をStringでこれもテストケースから(ry
  • 41. ロジックの設計unit/.../TextLoaderTest.groovy:@RunWith(TestRunner)class TextLoaderTest extends Specification { @Test def test_000() { when: def o = new TextLoader(new Activity(), Uri.parse( content://test-1 )) then: o.read() == Test file 1 } ...
  • 42. ロジックの設計unit/.../TextLoaderTest.groovy: ... @Test def test_001() { when: def o = new TextLoader(new Activity(), null) then: o.read() == } ...
  • 43. テスト失敗、実装を…
  • 44. 書いたはいいよ?でもContentResolverなんてどうやってテストする?
  • 45. Robolectric!独自クラスで置換することもできる→継承している必要すらないもちろんContentResolverも例外では…ないッ!
  • 46. ということでShadowContentResolverクラスを作成、ContentResolverとして振る舞わせることに。SCR.openInputStream(Uri): content://test-1 → Test file 1 と読めるInputStream その他 → nullTestRunner.bindShadowClassesに追記。
  • 47. ContentResolverの影unit/.../TestRunner.java:public class TestRunner extends RobolectricTestRunner { ... protected void bindShadowClasses() { super.bindShadowClasses(); Robolectric.bindShadowClass(ShadowSimpleAdapter.class); Robolectric.bindShadowClass(ShadowContentResolver.class); }}
  • 48. ContentResolverの影unit/.../ShadowContentResolver.java長いので割愛します m(_ _)mYouTubeのデモを参照して下さい…http://www.youtube.com/watch?v=nrT50tpIjZE
  • 49. 成功!次は!いよいよ実機で
  • 50. orz
  • 51. ぐぬぬぬぬ…MainActivityはいい…しかしViewActivityがピヨピヨしていることが判明なぜ?→AndroidManifest.xmlに宣言されていないらしいError in test_000:java.lang.RuntimeException: unable to resolve activity for Intent { action=... flg=... cmp=... /.ViewActivity } at ... at ...
  • 52. ぐぬぬぬぬ… (2)AndroidManifest.xmlにViewActivityの宣言を追加ついでにSDKバージョンの宣言も
  • 53. 成功!すっきり!
  • 54. まとめAndroid JUnitは実機でテストできるが、ビルドが非常に遅いGroovyはJVMで動作する簡潔・動的な言語→静的な型に縛られない書き方ができるのでテスト向き→実装の関係上、Android開発で使用するのはまだ難しい…実機で動かさないテストならGroovyで十分にできる→TDD/BDDが現実的に!
  • 55. まとめJVM上でAndroidシステムクラスは本来テストできないが、Robolectricを使うとテスト可能になるActivityのテストを行なう場合にはユニットテストだけでなく、Android JUnitによる結合テストも併用すると良い
  • 56. まとめSpockを併用するとテスト仕様をより読みやすく記述できるRobolectric自体はまだパラメタライズドテストを扱えない→expect+whereのような華麗なテストはできない…→ランナーの問題なのでルールなどの形にできれば解決するかも→もちろんRobolectricを絡ませなければパラメタライズ可能!
  • 57. まとめJavaから呼ばれるクラスはGroovyでモックできないので、Mockitoなどのモックフレームワークを併用する必要があるRobolectricを使うと任意のクラスをAndroidシステムクラスとして振る舞わせることができるAndroidのクラスをモックするにはRobolectric自前のクラスをモックするにはモックフレームワーク
  • 58. まとめAndroid Ant Runnerを使うとテスト環境を簡単に整備できるhttp://github.com/taky/android-runner (BSD)
  • 59. ご静聴ありがとうございました。Credits: Music by "FIGHTER X” – "Time to die" in album “REBOOT2,” © 2012 chipcon.org, used under the Creative Commons 3.0 Attribution-NonCommercial-NoDerivs Unported license: http://creativecommons.org/licenses/by-nc-nd/3.0/