[Android]kotlin+firestore emulatorを使ったunit testを構築する

こんにちは。virapture株式会社のもぐめっとです。

![IMG_76DCF03199D4-1.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42234/7c0e41b9-c3a2-e476-99ab-10846b5eaf02.jpeg)

最近もっぱら休みの日は山にこもって雪山に土下座する毎日なので自分の顔写真がないのが最近の悩みです。

本日はfirestoreのエミュレータを使ったunit testの方法について紹介します。

## 事前準備

firebaseコマンドでエミュレータを起動できるようにしておきます。

```shell
npm install -g firebase-tools
cd {firebase.jsonを定義してるところがあるのディレクトリ}
```

firebase.jsonがなければ下記コマンドでプロジェクトの設定をしましょう。

```sh
firebase init
```

emulatorを起動します。

```
firebase start emulator
```

これで準備おk！

## unit testの準備

肝としては2点あります。
それが、emulatorを使うようにする設定と、testの度にデータを削除する仕組みです。

emulatorを使うようにするには下記のような書き方を行います。

```kotlin
val firestoreHost = "10.0.2.2"
val firestorePort = 8080
val db = Firebase.firestore.apply {
    this.useEmulator(firestoreHost, firestorePort)
    this.firestoreSettings = firestoreSettings {
        isPersistenceEnabled = false
    }
}
```

データの削除はどうやるかなんですが、実はemulatorにはAPIがあって、それを叩くことでデータの削除を行います。

```kotlin
suspend fun deleteData() = withContext(Dispatchers.IO) {
    val projectName = "hoge"
    val url =
        URL("http://${firestoreHost}:${firestorePort}/emulator/v1/projects/${projectName}/databases/(default)/documents")
    val con = (url.openConnection() as HttpURLConnection).apply {
        this.requestMethod = "DELETE"
        this.instanceFollowRedirects = false
        this.connectTimeout = 5000
        this.readTimeout = 5000
        this.setRequestProperty("Content-Type", "application/json; charset=utf-8")
    }
    con.responseCode
    con.disconnect()
}
```

ということでこれらをいい感じに纏めたUtilを用意しておきます。

```kotlin
class FirestoreTestUtil {
    companion object {
        private const val firestoreHost = "10.0.2.2"
        private const val firestorePort = 8080
        val db = Firebase.firestore.apply {
            useEmulator(firestoreHost, firestorePort)
            firestoreSettings = firestoreSettings {
                isPersistenceEnabled = false
            }
        }

        suspend fun deleteData() = withContext(Dispatchers.IO) {
            val projectName = "hoge"
            val url =
                URL("http://${firestoreHost}:${firestorePort}/emulator/v1/projects/${projectName}/databases/(default)/documents")
            val con = (url.openConnection() as HttpURLConnection).apply {
                requestMethod = "DELETE"
                instanceFollowRedirects = false
                connectTimeout = 5000
                readTimeout = 5000
                setRequestProperty("Content-Type", "application/json; charset=utf-8")
            }
            con.responseCode
            con.disconnect()
        }
    }
}
```

## 実際にエミュレータを使ったunit testを書く

先程作ったutilを使ってこんな感じで書きます。

```kotlin
@ExperimentalCoroutinesApi
class CustomerRepositoryTest {
    private var db = FirestoreTestUtil.db

    companion object {
        @BeforeClass @JvmStatic
        fun setup() {
            runBlocking {
                FirestoreTestUtil.deleteData()
            }
        }
    }
    @Before
    fun before() {
        val collection = db.collection("customers")
        val customerData = mapOf(
            "firstName" to "Test",
            "lastName" to "Customer",
            "createdAt" to Timestamp.now(),
            "updatedAt" to Timestamp.now()
        )
        collection.document("testcustomer").set(customerData)
    }

    @After
    fun tearDown() {
        runBlocking {
            FirestoreTestUtil.deleteData()
        }
    }

    @Test
    fun findById() = runTest {
        val repository = CustomerRepository(db)
        val customer = repository.findById("testcustomer")
        assertNotNull(customer)
        customer?.run {
            assertEquals("Test", firstName)
            assertEquals("Customer", lastName)
        }
    }
}
```

## まとめ

androidとfirestoreでもemulatorを使ったunit testを無事書くことができるようになりました！
削除するときに癖がちょっとありますが、これでいい感じにできるはずです！

これでよいTDDライフを送れることでしょう！

最後に、[ワンナイト人狼オンライン](https://online.1nite-jinro.com/link/blog)というゲームを作ってます！よかったら遊んでね！

https://online.1nite-jinro.com/link/blog

他にも[Camecon](https://camecon.me/)や[Offcha](https://offcha.com/)といったサービスも作ってるのでよかったら使ってね！

https://camecon.me/

https://offcha.com/

また、チームビルディングや技術顧問、Firebaseの設計やアドバイスといったお話も受け付けてますので御用の方は弊社までお問い合わせください。

https://virapture.com/