Androidテスト全書のまとめ

ユニットテストをまったく書けないので**Androidテスト全書**という本を買った。

１章から2章の途中までしか読みきれてないため、そこまでのまとめ。

随時追記していく。

# テストの種類
## ユニットテスト
クラスごとにメソッドが期待通りの値を返すか、プロパティが適切に変化するか、を保証されるために書かれる

### Local Unit Test
実機はエミュレータを使用せず開発マシンから直接高速に実行できるテスト。
実行速度は早い。

### Instrumented Unit Test
実機やエミュレータを使ってユニットテストを実行する手法。
実際にユーザーが触れる形式に変換してAndroid上で実行する。
実行速度が遅い。

## UIテスト
ユーザーが実際にアプリのUIを操作したときに画面が正しく反応できるか検証するためのテスト。

# テストのピラミッド

ピラミッドの下層であればあるほどテストサイズが小さくて量が多く、頻繁に実行すべきである

![](https://developer.android.com/images/training/testing/pyramid_2x.png)

引用元
Fundamentals of Testing  |  Android Developers https://developer.android.com/training/testing/fundamentals

# テストの作り方

### 1. Android Studioでテスト対象クラスの上で `Command+Shift+T`

`Create New Test...`を選択
既にテストが存在すれば表示される

<img width="331" alt="スクリーンショット 2018-11-12 23.15.33.png" src="https://qiita-image-store.s3.amazonaws.com/0/52227/1a72a833-8d26-255e-9dde-5cb93cce5ebf.png">

作成ダイアログの矢印をONにしてOK

<img width="521" alt="スクリーンショット 2018-11-12 20.27.23.png" src="https://qiita-image-store.s3.amazonaws.com/0/52227/46293aa2-fad0-5c79-f414-c74b41f86b6c.png">

ディレクトリ選択ダイアログ
`test`配下に作成するようにする。

<img width="599" alt="スクリーンショット 2018-11-12 20.26.26.png" src="https://qiita-image-store.s3.amazonaws.com/0/52227/5f99c6b7-1566-bc7b-4222-27f6c599b914.png">

# ユニットテスト実装

## アサーション
JUnit4では `org.junit.Assert.assertThat()`メソッドを使って
assetThat(実測値, マッチャー（期待値））という形式でアサーションを行う。

## マッチャー
assetThatメソッドの第２引数に「期待する状態がどのような条件か」を定義した**マッチャー**を指定する。
JUnit4には **Hamcrest** というマッチャーライブラリが組み込まれており、豊富なマッチャーを利用できる。

[Hamcrest](http://hamcrest.org/)

#### その他マッチャー
- より大きい：`greaterThan()`
- Collectionが期待値を含んでいる：`hasItem()`

## 実装したユニットテスト

[Android-Kotlin-Lab/InputCheckerTest.kt at unit_test · banbara23/Android-Kotlin-Lab · GitHub](https://github.com/banbara23/Android-Kotlin-Lab/blob/unit_test/app/src/test/java/com/ikemura/android_kotlin_lab/InputCheckerTest.kt)

## テストをコマンドラインで実行

```
./gradlew test
```

より詳しい使い方
https://d.android.com/studio/test/command-line

## テストケースごとの初期化・後処理

JUnit4では初期化処理の共通化のために `arg.junit.Before`アノテーションが用意されている。
`@Before`アノテーションで修飾されたメソッドは各テストケースが走る直前に毎回実行される。

## テストケースの例外を検証

メソッドが`null`を受け取ったらIllegalAugumentExceptionをthrowする事を検証する場合、
JUnit4では`@Test(expected = 例外クラス)`として対象メソッドが意図通り例外を発生させたか検証できる。

```kotlin

@Test(expected = IllegalArgumentException::class)  
fun isValid_givenNull_throwsIllegalArgumentException() {  
    target.isValid(null)  
}
```

メソッドが例外をthrowされなかったらテストケースは失敗と扱われる。
また、expectedオプションは例外を1種類しか区別できない。

## テストケースのスキップ
テストケースに`arg.junit.Ignore`アノテーションをつけると一時的にスキップする。

```kotlin

@Ignore("仮実装なので一時的にスキップ")  //理由を書くと分かりやすい
@Test  
fun temporarilySkipThisTest() {  
    //略  
}
```

@Ignoreがついたテストケースは長時間放置されるのは望ましくない、一時的に利用するのみにとどめる。

## JUnit4のテストランナー

JUnit4では**テストランナー**（Test Runner)を使ってそれぞれのテストケースを収集・実行し、結果をユーザーに知らせる。
JUnit4ではテストクラスに対してテストランナーを切り替える事ができる。
`arg.junit.runner.RunWith`アノテーションにテストランナークラスを指定する。

```kotlin

@RunWith(JUnit4::class)  // ←JUnit4標準のテストランナー、省略したらこのランナーが使われる
class InputCheckerTest {
}
```

#### 他にもテストランナーがいくつかある
- AndroidJUnitRunner
  - Instrumented Testを実行するためのAndroid標準のテストランナー
- MockitoJUnitTestRunner
  - Mockitoで使うテストランナー
- RoboletricTestRunner

## JUnit4のテストルール

JUnit4でテストの実行方法を制御するために、**テストルール**という仕組みが提供されている。
テストルールは、各テストケースの実行前後にフックしてテストのメタ情報にアクセスできるので、@Beforeや@Afterアノテーションで行っていた初期化や終了処理をプラグインのように切り出して再利用できる。

# AssertJを使ったアサーション

AssertJとは、メソッドチェーンを利用したJava向けのアサーションライブラリ。
Hamcrestよりも自然言語に近く読みやすいアサーションを書くことができる。

## AssertJを使うには
app/build.gradleにAssertJの依存関係を追加する。

```groovy:app/build.gradle

dependencies {
  testImplementation 'junit:junit:4.12'  
  testImplementation 'org.assertj:assertj-core:3.10.0'  
}
```

## AssertJを使用したテスト

```kotlin

import org.assertj.core.api.Assertions.assertThat

@Test  
fun isValid_givenAlphaNumeric_returnTrue() {  
    val actual = target.isValid("abc123")  
    assertThat(actual).isTrue()  // <=AssertJで書き換えた
}
```

## 文字列のアサーション

"TOKYO"という文字列を様々な条件で検証する。
AssertJでは複数のアサーションメソッドチェーンでいくつも並べて書くことができる。

```kotlin

assertThat("TOKYO")  
  .`as`("TEXT CHECK TOKYO") 
  .isEqualTo("TOKYO")  
  .isEqualToIgnoringCase("tokyo") 
  .isNotEqualTo("KYOTO")  
  .isNotBlank()  
  .startsWith("TO")  
  .endsWith("YO")  
  .contains("OKY")  
  .matches("\[A-Z\]{5}")  
  .isInstanceOf(String::class.java)
```

メソッドチェーンで並べて書いた場合はAND条件となり、上から順に評価されて偽になったところで即座にテストケースは失敗となる。
もしアサーションのうちいずれかが失敗しても最後までテストケースを実行したい場合は`SoftAssertions`を利用する。

```kotlin

SoftAssertions().apply {
  assertThat("TOKYO")  
    .`as`("TEXT CHECK TOKYO") 
    .isEqualTo("TOKYO")  
    .isEqualToIgnoringCase("tokyo") 
    .isNotEqualTo("KYOTO")  
    .isNotBlank()  
    .startsWith("TO")  
    .endsWith("YO")  
    .contains("OKY")  
    .matches("\[A-Z\]{5}")  
    .isInstanceOf(String::class.java)
}.assertAll()
```

## 数値のアサーション

```kotlin

@Test
fun assertJ_numeric() {  
  assertThat(3.14159)  
  .isNotZero()    //ゼロではなく  
  .isNotNegative()    //負数ではなく  
  .isGreaterThan(3.0) //３より大きく  
  .isLessThanOrEqualTo(4.0)   //4以下  
  .isBetween(3.0, 3.2)    //3.0から3.2の範囲内  
  .isCloseTo(Math.PI, within(0.001))  //Math.PIで定義された円周率の定数から誤差0.001以内である
  }  
}
```

## コレクションのアサーション
AssertJで「要素が含まれているか」や「その順番はどうか」といった検証を直感的に扱うことができる。

```kotlin
val target = listOf("Giants", "Dodgers", "Athletics")  
assertThat(target)  
  .hasSize(3) //要素の個数を検証  
  .contains("Dodgers") //要素がリストに含まれるかどうか  
  .containsOnly("Athletics", "Dodgers", "Giants") //順不同で等価な要素のみが含まれているか  
  .containsExactly("Giants", "Dodgers", "Athletics") //等価な要素のみが同じ順序で同じ組み合わせで重複なしに含まれるか  
  .doesNotContain("Padres") //含んでいないか
```

### コレクションのフィルタリング

AssertJではコレクションから結果データを絞り込んだり（フィルタリング）、複雑なオブジェクトから調べたいプロパティだけをピックアップしたりといった検証に便利な機能が提供されている。

```kotlin
data class BallTeam(val name: String, val city: String, val stadium: String)  
  
val target = listOf(  
    BallTeam("Giants", "San Francisco", "AT&T Park"),  
    BallTeam("Dodgers", "Los Angels", "Dodger Stadium"),  
    BallTeam("Angels", "Los Angels", "Angel Stadium"),  
    BallTeam("Athletics", "Oakland", "Oakland Coliseum"),  
    BallTeam("Padres", "San Diego", "Petco Park")
)
  
assertThat(target)  
  .filteredOn { team -> team.city.startsWith("San") } //Sanで始まり  
  .filteredOn { team -> team.city.endsWith(("Francisco")) } //Franciscoで終わるものをフィルタリング  
  .extracting("name", String::class.java) //name:Stringプロパティだけを取り出す  
  .containsExactly("Giants")  //extractingで取り出されたname要素はGiantsである  
  
assertThat(target)  
  .filteredOn { team -> team.city == "Los Angels" } //cityがLos Angelsのものをフィルタリング  
  .extracting("name", "stadium") //nameとstadiumだけ取り出す  
  .containsExactly(  
      tuple("Dodgers", "Dodger Stadium"), //tupleでこのプロパティだけをもった一時的な型として扱って比較する  
      tuple("Angels", "Angel Stadium")  
  )
```

## AssertJの例外検証
AssertJではJUnit4標準の`@Test(expected=例外クラス)`よりも例外を簡単かつ詳細に検証できる。

```kotlin
@Test  
fun assetJ_exception() {  
    assertThatExceptionOfType(RuntimeException::class.java) //キャッチしたい例外のタイプ  
  .isThrownBy { functionsMyThrow() } //例外をだす可能性のあるメソッドを指定  
  .withMessage("Aborted!")    //詳細メッセージの検証  
  .withNoCause()  //この例外がほかの例外経由で送出されていないことを検証  
}  
  
private fun functionsMyThrow() {  
    throw java.lang.RuntimeException("Aborted!")  
}
```

## AssertJまとめ
詳しくは公式をチェック
http://joel-costigliola.github.io/assertj/

## 余談：次世代アサーションライブラリの覇者
Googleが開発するアサーションライブラリ `Truth` はAssertJに似たインタフェースをもち、読みやすくKotlinとの相性もいいので細流よく候補の一つとなっている。

これ？
[GitHub - google/truth: Fluent assertions for Java](https://github.com/google/truth)

# テストダブルを使った依存コンポーネントの差し替え

# Mockito でテストダブルを便利に扱う