星１個のアプリなんて作りたくない

「 [Labomartの軌跡。構想から運用まで](https://note.com/unotovive/n/n578baf44da60) 」 @unotovive
↑ [ゆめみ Advent Calendar 2019](https://qiita.com/advent-calendar/2019/yumemi) の前回の記事

# はじめに

Android アプリ開発者のみなさん、Google I/O 2018 の下記のセッションは見ましたか？

ちなみに、私は、、、、、、見てないです :rolling_eyes:

セッションでは、Play Store で星１レビューのついた原因分析を公開して

原因の４２% は**「アプリのクラッシュを含む安定性の問題」**だったみたい。

[<img width="580" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165251/8409fc0d-10ce-d90b-4629-c4d02d8877ca.png">](https://youtu.be/gpoqgQPwu_k?t=70)
出典： Youtube (Improve app performance and stability with Firebase (Google I/O'18))

## 星１レビューもらいたくない

テストを書きましょう笑

よくテストを書く時間がないと聞きますが、、

たぶん仕様調整やテストの時間がないだけです笑

きちんと仕様調整やテストの時間をもらいましょう :no_good: 

そして、プログラムや仕様のバグを洗い出すのです :sunglasses:

## 千里の道も一歩から

テストを書くことは思っているより簡単。

まずは１つテストを書いてみましょう！！

# テストって？

テストの種類には下記のようなものがあって

- 単体テスト
- 結合テスト
- UI テスト

今回は**単体テスト**に焦点を当てて話をしていきます！

## Android の単体テスト

Android の単体テストには下記のようなものがあります

- Local Unit Test
- Instrumented Unit Test

何が違うん？？って感じですが、簡単にいうと実機やエミュレーターが必要かどうかです。

今回は、実機やエミュレーターが必要ない **Local Unit Test** をやっていきます！

# 強い意志でテストを書こう

テストを書くのに強い意志は必要ありませんw

なんか物足りなかったので付けました笑

さて、テストを書いていこうと思いますが

せっかくなので最新の技術を使っちゃいます :nerd:

今回は **[JUnit5](https://junit.org/junit5/docs/current/user-guide/)** と **[Truth](https://github.com/google/truth)** を使います！

※ Truth は assertThat 使うために入れてます。ただそんだけですw

## 準備

ここからやっとコード書いていきます〜

下記の変更をして、Sync Now しちゃえばおk

※ **ExampleUnitTest.kt は要らないので削除してね**

```app/build.gradle
android {
    ...
+   kotlinOptions {
+       jvmTarget = '1.8'
+   }
}

dependencies {
    ...
-   testImplementation 'junit:junit:4.12'

+   testImplementation 'org.junit.jupiter:junit-jupiter-api:5.5.2'
+   testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.5.2'
+   testImplementation "org.junit.jupiter:junit-jupiter-params:5.5.2"
+   testImplementation 'com.google.truth:truth:1.0'
}
```

## テストするもの

今回は下記のような String の拡張関数作って

よくあるパスワードチェック処理をテストしていきます！

```extensions/StringExtension.kt
package {自分のパッケージ名}.extensions

/**
 * パスワードの条件
 *
 * 半角英小文字・大文字・数字をそれぞれ１種類以上含む
 * ８文字以上１０文字以下
 */
fun String.isPassword(): Boolean {

    val regex = Regex("""
        ^(?=.*?[a-z])(?=.*?[A-Z])(?=.*?\d)[a-zA-Z\d]{8,10}${'$'}
    """.trimIndent())

    return matches(regex)
}
```

## テスト実行！！！！

test パッケージの下に下記のファイルを作成します！

```extensions/StringExtensionKtTest.kt
package {自分のパッケージ名}.extensions

import org.junit.jupiter.api.Test

import com.google.common.truth.Truth.assertThat

internal class StringExtensionKtTest {

    @Test
    fun isPassword() {

        val password = "1234abcdAB"

        assertThat(password.isPassword()).isTrue()
    }
}
```

そして、isPassword() メソッドの左にある実行マーク押して

テストを動かしてみてください！

テストが成功すると気持ちいいですよね！

※ **テストが成功する度に緑のチェックマークがクセになっていきます**

## 複数パターン一気にテストしたいよ

さっきのテストでは１つしかテストできてない。。

一気に10個とかやっちゃいたい、、どうすれば、、、

はい、そんなときのために **ParameterizedTest** があります :sunglasses:

使ってみましょ〜

```extensions/StringExtensionKtTest.kt
package {自分のパッケージ名}.extensions

import com.google.common.truth.Truth.assertThat
import org.junit.jupiter.api.extension.ExtensionContext
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.Arguments
import org.junit.jupiter.params.provider.Arguments.arguments
import org.junit.jupiter.params.provider.ArgumentsProvider
import org.junit.jupiter.params.provider.ArgumentsSource
import java.util.stream.Stream

internal class StringExtensionKtTest {

    @ParameterizedTest
    @ArgumentsSource(TestCase::class)
    fun isPassword(password: String, expected: Boolean) {

        assertThat(password.isPassword()).isEqualTo(expected)
    }

    private class TestCase : ArgumentsProvider {
        override fun provideArguments(context: ExtensionContext?): Stream<out Arguments> = Stream.of(
            arguments("1234abcdAB", true),
            arguments("abcdAB1234", true),
            arguments("1234abcdab", false)
        )
    }
}
```

はい、JUnit5 最高

これで複数ケース簡単にテストできますね♪

じゃんじゃんテストケース追加していってください :relaxed:

## 実は、、

テストが失敗するケースがあります笑

みなさん見つけられましたかね？

見つけられていない方はテストケース追加しまくってくださいね :heart: 笑

# おわりに

ちゃんとテストを書いて、少しでもバグを減らして

星５のレビューたくさんもらえるようになりましょ〜 :star2: 

明日は @ihcamonoihS さんの記事 :hand_splayed: 

## 参考文献

- [Android テスト全書](https://peaks.cc/books/android_testing)
- [言語別：パスワード向けの正規表現](https://qiita.com/mpyw/items/886218e7b418dfed254b)
- [Kotlin で JUnit 5 を使ってみた](https://qiita.com/navitime_tech/items/a66f553af224eaff490a)