Androidのテストツールならこれ!Robolectric導入の儀。
かつてはAndroid依存のモジュールのテストはド遅いからPOJOとうまく分離してなるべくPOJOを通常のUnitテストで・・・とか思っていた時期があったんだけど、この分離ってやつが相当キツくて。ンカスプログラマだから。
つまりテスト書いてなかったんだけど、それはまだAndroid開発のノリがわかってなかったこともあったし、開発対象が画面の遷移周りがメインであまり小難しい処理がなかったんで、テストによるメリットよりデメリットが上回る可能性があったからで。
今はかなりわかってきたし、細かい実装もやっていかにゃあ、なんでもう一度テストについてよく調べてみたのよ。
そもそもAndroidモジュールが絡むテストが遅いのは
- Googleがandroid.jarにある全メソッドを"throw new RuntimeException("Stub!");"に変えている
- そのため、エミュレータなり実機なりで動かさないといけない(モックに差し替える前に例外が飛ぶのでMockingもりーむー)
- ということはDex(確かDalvikVMでうごくバイトコードにする処理)やらインストールやらが必要なのでド遅い。
ということなんだけど、なんと!黒魔術にてこの"throw new RuntimeException("Stub!");"を差し替えてよしなにやってくれるフレームワークがありました!
Robolectric
この会社の人たちはIntelliJを使ってます!イェー!
で、IntelliJに途中から導入する手順も公式に書いてあるんだけど、なんかよくわからない手順だったので無視して勝手にやってうまくいった方法を以下に。
pomにちょろっと足す
<!-- TEST DEPENDENCIES --> <!-- Robolectric --> <dependency> <groupId>com.pivotallabs</groupId> <artifactId>robolectric</artifactId> <version>1.1</version> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> <scope>test</scope> </dependency>
これ、なるべく上にもっていってほいたほうがいいです。理由は後述。
依存関係の設定をする
Project Setting > Modules > アプリ > Dependenciesタブ
で、テスト関係のモジュールをAndroidSDK(黄色い玉のやつ)より上に持っていく。これをしないと"java.lang.RuntimeException: Stub!"がでる。で、さっきpomでテストモジュールの記述をなるべく上に、という話をしたのは、この上に持っていく作業がちょっとだけ楽だから。だからやっぱりpom上では上に持っていかなくてもいいです。
Sourceの設定をする
Project Setting > Modules > アプリ > Sourcesタブ
で、Source Foldersがsrc/main/java、Test Source Foldersがsrc/test/javaになっているようにする。Mavenのスタンダードストラクチャーってやつね。他のがあったらもしかしたら邪魔になるかもしれないから、要らないと思ったら消しておく。
テストを作る
とりあえず適当に。
import com.xtremelabs.robolectric.RobolectricTestRunner; import org.junit.Test; import org.junit.runner.RunWith; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.junit.Assert.assertThat; /** * User: ria10 * Date: 2012/11/27 * Time: 16:35 */ @RunWith(RobolectricTestRunner.class) public class MyActivityTest { @Test public void shouldNotBeNull() throws Exception { MyActivity activity = new MyActivity(); assertThat(activity, is(notNullValue())); } }
ということで、@RunWith(RobolectricTestRunner.class)を書くことがRobolectricを使う、ということになります。この超簡単なテストではそれ以外にRobolectric依存の記述はありません。
実行
超フツーに実行するだけ。
ただ、
java.lang.RuntimeException: java.lang.ClassCastException: com.xtremelabs.robolectric.RobolectricTestRunner cannot be cast to com.xtremelabs.robolectric.internal.RobolectricTestRunnerInterface
が出たら、たぶんそれはお使いのJUnitのバージョンがRobolectricにあってないから。4.11はダメ。4.10なら大丈夫。
https://groups.google.com/forum/#!msg/robolectric/dtvMJSr5HkQ/VA3F3ZcCmekJ
pom書き換えると、DependenciesのところでまたTestモジュールたちがAndroidSDKより下にきちゃうから再設定が必要なのが微妙だけど仕方ない。
イェー!!ハッピーテスティング!