本記事では、Robolectricを使ってAndroid端末を使わないユニットテストを行うためにどんなことをすればいいかをまとめた記事です。これを機にRobolectricに興味を持っていただければ幸いです。というよりAndroid端末を使わないでテストやってみよう!って思えればいいかなと思っています。
Robolectricとは?
JVM上でAndroid依存部分のテストを行うためのライブラリです。
JVM上で動かすことができるので
・Android端末を用意せずともActivityやContextが絡むコードのユニットテストを行うことができます。
そして、
・Android端末を使うのよりテストコードの実行時間がはやいです!
公式ホームページは
http://robolectric.org/
です。
執筆時点(2016/2/6)での最新バージョンは3.0です。
今回のテスト対象のアプリ
今回テストに使うアプリは「Permutation計算アプリ」です。二つのパラメータを入力して計算ボタンをタップすると計算結果が表示されるものです。(ちなみにPermutationとは高校数学でいう「順序」のことです。詳しくはこちらを参考にしてください。)
ソースコードはGithubにアップロードしています。
https://github.com/LyricalMaestro/RobolectricPractice
クラス構成
app/main/java内のファイル構成をAndroidStudioで表示したものをいかに貼り付けてみました。このアプリはGUI部分を担う「MainActivity」とPermutationの計算を担う「Calculator」(Androidでなくても普通のJVM上で動きます)の二つのクラスから構成されています。
テストコードを書いていく
ここから先ほど紹介した「MainActivity」と「Calculator」についてテストを書いていきます。Androidフレームワークを使っていない部分の方が簡単にテストが書けるのでまず「Calculator」からテストを書いていきます。
AndroidでJUnitのコードを書くには?
プロジェクトのデフォルトの設定だとキャプチャ1のように、javaフォルダとして認識されず、テスト実行はおろか自動ビルドも行われないのでテスト効率が下がります。
テストコード作成作業効率化のための設定を行います。AndroidStudio左下にある「Build Variants」のタブをクリックします。
そしてBuild VariantsパネルのTest Artifactのところを「Unit Tests」に切り替えます。
そうするとキャプチャ2のようにtest/javaフォルダがソースコードのフォルダとして認識されます。
gradleファイルの設定
Robolectricを使うのでbuild.gradleにRobolectricをインポートするコードを記載します。テストを書くのにJUnitもインポートしないといけないのでは?と思うかもしれませんが、Robolectricをインポートすれば芋づる式にJUnit4もインポートされます。(これはRobolectricを使うのでればJUnit4を使ってテストコードを書くべし、と言われているのと同じことだと思ってください。)
dependencies {
…略…
testCompile "org.robolectric:robolectric:3.0"
}
モデル部分のテストコードを書く
モデル部分に相当する「Calculator」クラスのテストコードを書いていきます。Calculatorクラス自体はAndroidに一切依存しないクラスなので普通のJUnit4のテストコードを書くノリでかけます。
Androidフレームワークに依存したコードを書いていく
MainActivityのテストコードを書いていきます。ここはバリバリAndroidに依存するコードで構成されているのでRobolectricを使ってテストを書いていきます。
/**
* MainActivityのテストコード
*
* Created by LyricalMaestro on 2016/02/06.
*/
@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class, sdk = Build.VERSION_CODES.LOLLIPOP)
public class MainActivityTest {
@Test
public void calculateBtn() {
MainActivity mainActivity = Robolectric.setupActivity(MainActivity.class);
setValueToEditText(mainActivity, R.id.input_n, "5");
setValueToEditText(mainActivity, R.id.input_m, "3");
mainActivity.findViewById(R.id.calculate_btn).performClick();
TextView resultView = (TextView)mainActivity.findViewById(R.id.result_view);
assertThat(resultView.getText().toString(), is("5P3 = 60"));
}
private void setValueToEditText(MainActivity mainActivity, int viewId, String value) {
EditText editText = (EditText)mainActivity.findViewById(viewId);
editText.setText(value);
}
}
class側にアノテーションをつけているのと、Activity生成時にRobolectricのクラスを使っているところ以外は普通にJUnit4のノリで書けます。メソッドの中身は公式ホームページのサンプルコードみながら簡単なものはサクッとできますが、アノテーション周りについては知らないとはまってしまう可能性があるので以下、そこだけ特記します。(完全な私の体験談だったりしますが)
1. @Runner
にはRobolectricGradleTestRunner.class
を指定すること
公式ホームページのチュートリアルではRobolectricTestRunner.class
が使われていますが、これだとresフォルダ、assetsフォルダ、ひいてはAndroidManifest.xmlすらも使われなくて、レイアウト関連のテストがこのままだとできなくなります。これに対する方法は「カスタムTestRunner」を実装しなければいけません。
RobolectricGradleTestRunnerは上記の問題に対する対応をしっかり行ったカスタムTestRunnerです。
2. @Config
にはsdkを指定しておくとはまらずに済む
sdkオプションをつけない場合、app/build.gradleのtargetSdkVersionの番号で動きます。Robolectricは現段階でバージョン16,19,21でのみ対応していますが、targetSdkVersion=23とかやってしまうとRobolectric側が「対応していません!」というエラーがでてテストが実行できません。
なのでsdkオプションをつけて明示的にどのバージョンで動かすかを指定してあげる必要があります。
ユニットテストを実行する
ユニットテストを実行する方法は以下の二通りです。
1. コマンドラインから実行
ターミナル上で./gradlew test
を実行すればOKです。(もちろんプロジェクトフォルダまで移動してから行うことですが)
テスト結果のレポートはapp/build/reports/testsフォルダ内に保存されます。
(ターミナル上でopen app/build/reports/tests/[debug or release]/index.html
を実行するとテスト結果のレポートがブラウザ上で確認することができます。)
2. AndroidStudio上から実行
慣れないとちょっと大変ですが慣れればやりやすいと思います。
まず、Build VariantsパネルのTest Artifactを「Unit Test」にしておいてください。その上で話をすすめていきます。
JUnitのデフォルト設定の変更
これは最初の一回だけで大丈夫です。
- AndroidStudio上部のツールバーから「Edit Configurations…」を選択。
- [Defaults]-[Junit]タブを選択。その後、Working directoryの部分を「$MODULE_DIRS」に書き換えて、Apply.
JUnit実行設定の作成
- テストを行いたい対象を選択し、右クリック。メニューの「Select 'XXXXXX'」をクリック。(すると、テスト実行設定に追加されます。)
- そのまま、Run 'XXXXXXX'を実行すればユニットテストが実行されます。
結果についてはapp/build/reports/testsフォルダ内に保存されます。
Android端末を使ったテスト(AndroidJUnit4)は完全に「いらない子」か?
Robolectricは素晴らしいフレームワークですが、これでユニットテストの全てを賄えるわけではありません。特別な端末について実際テストするとき、もしくはAndroid6.0代でテストしたいときなどはAndroidJUnit4を使います。
この両方を使い分けるためにtestフォルダ, androidTestフォルダ両方にテストコードを書かないといけません(Androidフレームワークに依存するところだけは)。これはメンテナンスする上ではかなり厄介な構造になります。
それを解決するための方法が以下のページで紹介されていますが、実際試していないのでどこまですごいかはわかりません。ただ使えるなら使ってみてはいかがでしょう。
まとめ
Robolectricをうまく使うことによってAndroid端末なしでユニットテストを行うことができます。その環境を構築する手順、実際のやり方について今回具体的なプログラムを介して説明しました。
私自身、このRobolectricを使って数日しか経っていないのでRobolectricで他にどんなことができるかがきちんとは把握しきれていません。今後使っていきながら何ができるかを把握し、Android端末なしユニットテストの環境をバシバシ整備していきたいと思います。