AndroidのUnitTestのJUnit4移行とカバレッジ計測(+Mockitoの依存関係解決)

# 背景
AndroidにてUnitTestを書く際には、ApplicationTestCaseとかActivityInstrumentationTestCase2とかServiceTestCaseなどを使っていたと思うが、Android 7からこれらは非推奨になったので、書き換える必要がある。
また、従来はJUnit3しかサポートしていなかったが、新方式ではJUnit4が使えるようになったので、値の検査がやりやすくなっている。

# 本記事のスコープ
Android公式のの[Building Effective Unit Tests](https://developer.android.com/training/testing/unit-testing/index.html Building Effective Unit Tests)には、

- Local Unit Test
- Instrumented Unit Test

の二つが紹介されているが、この記事ではInstrumented Unit Testについて記述する。ついでに、カバレッジ計測をしたいという人も多そうなのでその設定についても触れる。

Local Unit Testはいずれ書くかもしれない。

# 従来方式からの移行
## セットアップ
基本的には[Building Instrumented Unit Tests](https://developer.android.com/training/testing/unit-testing/instrumented-unit-tests.html Building Instrumented Unit Tests)に書いてあるが、文章量多いので以下にまとめておく。

### dependenciesの追加
build.gradleの`dependencies`ブロックに以下を追加。

```groovy:build.gradle
dependencies {
    androidTestCompile 'com.android.support:support-annotations:24.0.0'
    androidTestCompile 'com.android.support.test:runner:0.5'
    androidTestCompile 'com.android.support.test:rules:0.5'
    // 以下任意
    androidTestCompile 'org.hamcrest:hamcrest-library:1.3'
}
```
`com.android.support:support-annotations:24.0.0`等は、現在のbuild.gradleに指定している`buildToolsVersion`によって変える必要があるので、Android Studio上でエラーが出たら書き換える。

`androidTestCompile 'org.hamcrest:hamcrest-library:1.3'`
も書いておくと、assertThatによる検証で便利なメソッドが追加される。

### AndroidJUnitRunnerの指定

build.gradleの`defaultConfig`ブロックに以下を追記。

```groovy:build.gradle
android {
    defaultConfig {
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
}
```

### @RunWithの記述
テストクラスの定義前に、`@RunWith(AndroidJUnit4.class)`を記述する。ApplicationTestCaseとか継承しているなら、継承をやめて、コンストラクタも消す。

```java
import android.support.test.runner.AndroidJUnit4;

@RunWith(AndroidJUnit4.class)
public class LogHistoryAndroidUnitTest {
}
```

### setUp(), tearDown()の置き換え
JUnit3では、

```java
protected void setUp() {}
protected void tearDown() {}
```

の二つのメソッドを使って、各テストケースの実行前、実行後に行う提携処理を定義していた。
JUnit4では、メソッド名に命名規則はなく、アノテーションで指定する。

```java
@Before
public void beforeExecute() {}
@After
public void afterExecute() {}
```

これらのアノテーションを指定するメソッドはpublic methodでないといけない。ゆえに、アノテーションだけでなく、メソッドの可視性もpublicに変更する必要がある。

```java
// メソッドはpublicにすること！

@Before
protected void setUp() {}
@After
protected void tearDown() {}
```
上記はエラーとなる。

### テストケースの書き換え
JUnit3では、メソッド名の接頭辞がtestとなっているメソッドをテストメソッドと認識していた。

```java
public void test_CheckValue() {
    assertEquals(hoge, 1);
}
```

JUnit4では、アノテーションを使って指定する。

```java
@Test
public void checkValue() {
    assertEquals(hoge, 1);
}
```


## 典型パターンごとの書き換え方法
### ActivityInstrumentationTestCase2の置き換え
[ActivityTestRule](https://developer.android.com/reference/android/support/test/rule/ActivityTestRule.html)を使う。以下のようにすればActivityが取得できるので、あとは良しなに。

```java
private Activity mActivity;

@Rule
public final ActivityTestRule<HogeActivity> mActivityTestRule = new ActivityTestRule<>(HogeActivity.class);

@Before
public void beforeEachTest() {
    mActivity = mActivityTestRule.getActivity();
}
```

### ServiceTestCaseの置き換え
[ServiceTestRule](https://developer.android.com/reference/android/support/test/rule/ServiceTestRule.html)を使う。以下のようにすればServiceを起動できるので、あとは良しなに。`bindService()`もあるので、bindしたければそちらを使う。

```java
@Rule
public final ServiceTestRule mServiceTestRule = new ServiceTestRule();

@Before
public void beforeEachTest() {
     mServiceRule.startService(
         new Intent(InstrumentationRegistry.getTargetContext(), HogeService.class));
}
```

### Contextの取得
[InstrumentationRegistry](https://developer.android.com/reference/android/support/test/InstrumentationRegistry.html)の`getContext()`と`getTargetContext()`を使う。`getContext()`は、テスト実施側のContextの取得を行うので、テスト対象のContextが欲しい場合は`getTargetContext()`を使うこと。
`ActivityInstrumentationTestCase2.getContext()`だと`InstrumentationRegistry`における`getTargetContext()`相当のものが取れていたはず。

### @UiThreadTestの置き換え
以下が使えるが、

- 新しい方の@UiThreadTest
- UiThreadTestRule.runOnUiThread(Runnable)
- ActivityTestRule.runOnUiThread(Runnable)
- InstrumentationRegistry.getInstrumentation().runOnMainSync(Runnable)

一番上の@UiThreadTestは特殊な副作用があるのでお勧めしない。二つ目の`UiThreadTestRule.runOnUiThread(Runnable)`を使おう。

使い方は簡単で、UI Threadで実行したい処理について、

```java
UiThreadTestRule.runOnUiThread(new Runnable() {
    // UI Threadで実行したい処理
});
```

とする。

以下に@UiThreadTestの副作用を書いておくが、長いので気になる人以外は読まなくていい。

#### @UiThreadTestの副作用

`android.support.test.annotation.UiThreadTest`で従来の@UiThreadTestっぽいことができる。ただし、UiThreadTestRuleやそれを継承したクラス(ActivityTestRuleなど)をnewしておかないと効果がない。

ただし、単純なリプレイスはできなくて、@UiThreadTestを指定してかつUiThreadTestRuleをnewしている場合は、@UiThreadTestを指定しているテストケースの実行時には@Beforeと@AfterもUiThreadで動作するようになる。@UiThreadTestを指定していないテストケースの実行時には@Beforeと@Afterと@Testはandroid.support.test.runner.AndroidJUnitRunnerで動作する。

この挙動は案外厄介で、例えば、Serviceのテストをする際に、@Beforeで該当サービスとbindしておきたいケースがあり、以下のようなコードを書いたとする。

```java
@Before
public void setup() {
  mServiceTestRule.bindService(intent, new ServiceConnection() {
      @Override
      public void onServiceConnected(ComponentName componentName, IBinder binder) {
          mService = ((SomeService.LocalBinder) binder).getService();
      }
  }
}
```

@UiThreadTestを指定していなければ、setupメソッドはAndroidJUnitRunnerのThreadで動作するが、ServiceはUI Threadで動作するため、setUpメソッドでbindService後に適宜待っていればonServiceConnectedが呼び出される。

しかし、@UiThreadTest指定すると、setupメソッドがUI Threadで動作するようになるため、setupメソッドがThreadを手放さない限りServiceは開始できない([警告: サービスは、そのホスト プロセスのメインスレッドで実行します。](https://developer.android.com/guide/components/services.html?hl=ja))。
しかし、setupメソッドを抜けてしまうと、ただちにテストメソッドが走りだしてしまうため、Serviceの接続を待てないというジレンマが生じる。
だから[UiThreadTest](https://developer.android.com/reference/android/support/test/annotation/UiThreadTest.html UiThreadTest)に以下のような記述があるんだなぁ。
> Note, due to current JUnit limitation, methods annotated with Before and After will also be executed on the UI Thread. Consider using runOnUiThread(Runnable) if this is an issue.

そんなめんどくさいこと考えたくなければ、`UiThreadTestRule.runOnUiThread(Runnable)`を使うのが良い。

# カバレッジの計測
## セットアップ
app/build.gradleの適当なビルドタイプに、`testCoverageEnabled true`を指定する。

```groovy:build.gradle
android {
    ....
    buildTypes {
        ....
        debug {
            testCoverageEnabled true
        }
    }
}
```

## 実行
`createDebugCoverageReport`タスクを実行する(flavor定義している場合は`create${FLAVOR_NAME}DebugCoverageReport`)
__一つでもFailするテストがあるとレポートが作成されないので注意__。
結果ファイルは、
`app/build/outputs/reports/coverage/debug`
に出力される。(flavor定義しているとちょっとパス変わる)

# assert例とテストの制御
## assert文のJUnit4化
`assertEquals(expected, actual)`のままでもテストは通るが、せっかくだしJUnit4スタイルにしたいという場合は、`assertThat(actual, is(expected))`に変更すればよい(expectedとactualの出現順序が変わっているので注意)。

JUnit4化の一番のメリットはassert文にて柔軟な検査ができるようになったことだと思うので、落ち着いたら下記の記事を読んでからテストケースを書くことをお勧めする。
[HamcrestのMatchersに定義されているメソッドの使い方メモ](http://qiita.com/opengl-8080/items/e57dab6e1fa5940850a3)

## テスト実行のタイムアウト設定
テスト実行するとデッドロックをしているのかうんともすんともいわなくなることがあるが、以下のようにタイムアウト時間を設定することで自動的に失敗と判定することができる。

```java
@Test(timeout=1000)
public void testWithTimeout() {
    // some test
}
```

## exceptionの検査
Exceptionが発生するのが正しいという場合には、以下のように記述することができる。
try-catchでもいいかもしれないけれど。

```java
@Test(expected = IndexOutOfBoundsException.class) 
public void empty() { 
     new ArrayList<Object>().get(0); 
}
```

## テストケースのタイプ分類と実行
テストケースを@SmallTest, @MediumTest, @LargeTestに分類して、実行時にどのタイプを走らせるか指定することができる。
[Android の Instrumented Test で指定のサイズのテストだけ実行する (@SmallTest とか@LargeTestとか)](http://vividcode.hatenablog.com/entry/android-app/atsl-test-size)

テストが素早く実行できれば、commitごとに自動で実施してデグレしてないかチェックするというようなことができるかもしれない。
しかし、テストケースによっては、httpアクセスや複雑なdb処理を行って、その挙動が壊れていないというようなことをチェックしたい場合もあるだろう。
このような場合は、素早く終わるテストを@SmallTestに分類し、時間がかかるけどユースケースに近いテストを@LargeTestに指定しておく。そして、commit毎では@SmallTestを、ブランチのマージ前には@LargeTestも含めて実施するというようなワークフローにしておくと良いかもしれない。

以下、Googleが提唱しているTestSizeという概念と、クックパッドの例の紹介記事。
[Googleが提唱するTestSizeとJava,MavenによるTestSizeの実現方法について](http://qiita.com/AHA_oretama/items/6239aac9eafd397ebf4e)

# Mockitoの導入
Mockitoを使えば、特定のメソッドの挙動を書き換えることができる。これは非常に便利そうで、多くの人が使おうとしたことがあるだろうし、実際にテストコードを書いたことがある人も多いと思うが、実行時にエラーになってしまってあきらめた人が多くいるはず(私は3回あきらめた)。
やれhamcrestが重複しているとか、dexmaker入れないといけないとか、dexmaker.cachedirを指定しないといけないとか・・・

最近、ついに導入に成功したので以下に書いておく。
Instrumented Test Caseで使いたい場合は、app/build.gradleに以下を指定する。

```groovy:build.gradle
androidTestCompile 'com.linkedin.dexmaker:dexmaker-mockito:2.2.0'
```

これだけでdexmakerもMockitoも導入されるので、すぐに使い始められる。しかも導入が難しいMockito2らしい。
Local Unit Testで使いたい場合はandroidTestCompileではなくtestCompileにすればよいはず。

PowerMockはいまだに実行時に落ちていて諦めている。。。まあMockitoの中の人もprivateメソッドを直接テストするなと言っているのでいいのかな。