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


Labomartの軌跡。構想から運用まで@unotovive
ゆめみ Advent Calendar 2019 の前回の記事

はじめに

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

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

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

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


出典: Youtube (Improve app performance and stability with Firebase (Google I/O'18))

星1レビューもらいたくない

テストを書きましょう笑

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

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

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

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

千里の道も一歩から

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

まずは1つテストを書いてみましょう!!

テストって?

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

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

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

Android の単体テスト

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

  • Local Unit Test
  • Instrumented Unit Test

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

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

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

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

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

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

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

今回は JUnit5Truth を使います!

※ 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

/**
 * パスワードの条件
 *
 * 半角英小文字・大文字・数字をそれぞれ1種類以上含む
 * 8文字以上10文字以下
 */
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() メソッドの左にある実行マーク押して

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

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

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

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

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

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

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

使ってみましょ〜

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 最高

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

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

実は、、

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

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

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

おわりに

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

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

明日は @ihcamonoihS さんの記事

参考文献