Unityでfirestore emulatorを使ったunit testを作る

こんにちは。[virapture株式会社](https://virapture.com)の[もぐめっと](https://mogmet.com)です。

![mogmet.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42234/0d8a5d0c-3d7d-3902-8e68-aaf5b7dcc0bd.jpeg)
京都で花見をしてみたらとても満開でめちゃくちゃ満足度高かかったのでまた行きたいと思いました。

本日はUnityとFirestore Emulatorを使ったUnit testのやり方について解説いたします。

## 事前準備

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

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

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

```sh
firebase init
```

emulatorを起動します。

```
firebase start emulator
```

これで準備おk！

## 前知識
### emulatorを使うようにする
unityのfirestore emulatorの使用はマニュアルには書いてないのですが、実は下記コードを記載することで使えるようになります。

```cs
var firestore = FirebaseFirestore.DefaultInstance;
if (firestore.Settings.Host == "localhost:8080")
{
    return;
}
firestore.Settings.Host = "localhost:8080";
firestore.Settings.SslEnabled = false;
```

https://github.com/firebase/quickstart-unity/issues/719#issuecomment-938759588

Hostの設定をチェックしていますが、既にインスタンスが設定されている時にHostを変更しようとするとエラーになってしまうため、チェックをしてから設定するようにしています。

### テストデータの削除

テストデータを一斉削除するにはemulatorに用意されているAPIを叩くことで削除できます。

```cs
UnityWebRequest request = UnityWebRequest.Delete(
            "http://localhost:8080/emulator/v1/projects/{プロジェクト名}/databases/(default)/documents");
await request.SendWebRequest();
```

テストの前とかに毎回リセットなどを行うといいと思います。

このあたりはios/androidとかでも同様なことが言えます。

https://qiita.com/mogmet/items/f4c480c690e479c0fd40

## テストを書く

ではfirestoreの前知識がわかったところで早速Unit Testを書いてみましょう！
今回はZenjectも絡めたテストを紹介します。

```cs
public class ViewModelTest : ZenjectUnitTestFixture
{
    [Inject] private IViewModel target = default!;
    private FirebaseFirestore firestore = default!;

    [OneTimeSetUp]
    public void setupOnce()
    {
        firestore = FirebaseFirestore.DefaultInstance;
        if (firestore.Settings.Host == "localhost:8080")
        {
            return;
        }
        firestore.Settings.Host = "localhost:8080";
        firestore.Settings.SslEnabled = false;
    }

    [SetUp]
    public void CommonInstall()
    {
        // テスト対象のオブジェクトを注入
        Container.BindInterfacesTo<ViewModel>().AsCached();
        Container.Inject(this);
    }
    
    private async UniTask setupData()
    {
        // 一回テストデータを削除してから
        UnityWebRequest request = UnityWebRequest.Delete(
            "http://localhost:8080/emulator/v1/projects/{プロジェクト名}/databases/(default)/documents");
        await request.SendWebRequest();
        // テストデータを投入
        await firestore.Document("rooms/1").SetAsync(new Dictionary<string, object> {{"name", "部屋名"}});
    }

    [UnityTest]
    public IEnumerator TestRoom() => UniTask.ToCoroutine(async () =>
    {
        await setupData();
        var room = await target.FetchRoom();
        Assert.AreEqual(room.name, "部屋名");
    });
}

```

unityでのtestはSetupでasync awaitを使うことができないので、UnityTestでデータの設定などを行うようにしています。
本当はSetupでそういったデータの準備ができると一番いいんですが残念です。

## まとめ

emulatorを使うことでunityでもfirestoreを絡めたテストを行うことができるようになりました。
Authenticationはまだemulatorには対応していないようなので、[Moq](https://github.com/moq/moq4)などを使ってmock化して処理すると良さげです。


最後に、[ワンナイト人狼オンライン](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/