コピペで使うFirebase【テスト編】

## 概要
firebaseを使用する際に、コピペで動かしたいと思い設定手順を投稿しようと思いました。

前回：[Firebase【立ち上げ編】](https://qiita.com/ovama-koffee/items/1dd1170268d277119e1a)
次回：[Firebase【CircleCI編】](https://qiita.com/ovama-koffee/items/f80b05c369075f8982fe
)

## jestのインストール＆設定
テストツールには[jest](https://jestjs.io/docs/ja/getting-started)を使用
※　本プロジェクトはルートのpackage.jsonとfunctions/package.jsonで設定が異なるため、functions内でjestをインストールします。

```console
$ cd functions
```

```console
# jestのインストール
$ npm install -D jest

# CLIでjestの初期設定する場合　基本はEnter連打
$ npm install -g jest
$ jest --init
  ...
# 設定項目
  ✔ Would you like to use Jest when running "test" script in "package.json"? … yes
  ✔ Choose the test environment that will be used for testing › node
  ✔ Do you want Jest to add coverage reports? … no
  ✔ Automatically clear mock calls and instances between every test? … no
```

## テストのセットアップ

```console
$ npm install -D fs
$ npm install -D @firebase/testing
$ firebase setup:emulators:firestore
```

## Firestoreのルールを設定
※ firestore/storageのルールは別で投稿予定

```diff:firestore.rules
service cloud.firestore {
  match /databases/{database}/documents {
-   match /{document=**} {
-     allow read, write;
-   }
+   match /message/{userId} {
+     // 認証時のみ読み込み・書き込みを許可
+     allow read, create: if request.auth.uid == userId
+   }
  }
}
```

## テストの作成
テストを格納するディレクトリーを作成
※jestではデフォルトで__tests__直下のファイルまたはファイル名が.spec.jsもしくは.test.jsで終了するファイルを実行します。
　今回は両方します❤️

```console
$ mkdir __tests__
$ cd __tests__
$ touch index.test.js && touch firestore.test.js
```
### HTTP Functionのテスト
```__tests__/index.test.js
const http = require('../index');

describe('helloWorld', () => {
    test('it returns a successful response', () => {
        const req = {}
        const res = {
            send: (payload) => {
                expect(payload).toBe("Hello from Firebase!")
            }
        }

        http.helloWorld(req, res)
    })
})
```

### Firestore Rulesのテスト
```__tests__/firestore.test.js
const firebase = require("@firebase/testing");
const fs = require("fs");

const projectId = "ProjectID";
const rules = fs.readFileSync("../firestore.rules", "utf8");

describe("firestore-test", () => {
    // はじめに１度ルールを読み込ませる
    beforeAll(
      async () => {
        await firebase.loadFirestoreRules({
          projectId,
          rules
        });
      }
    );
  
    // test毎にデータをクリアする
    afterEach(
      async () => {
        await firebase.clearFirestoreData({ projectId });
      }
    );
  
    // 全テスト終了後に作成したアプリを全消去
    afterAll(
      async () => {
        await Promise.all(
          firebase.apps().map((app) => app.delete())
        );
      }
    );
  
    function authedApp(auth) {
      return firebase.initializeTestApp({ projectId, auth }).firestore();
    }

    describe("messageコレクションのテスト", () => {
        test("messageの読み込みを実行", async () => {
          const db = authedApp({ uid: "testUser" });
          const message = db.collection("message").doc("testUser");
          await firebase.assertSucceeds(message.get());
        });
    
        test("messageへ書き込みを実行", async () => {
          const db = authedApp({ uid: "testUser" });
          const message = db.collection("message").doc("testUser");
          await firebase.assertSucceeds(
            message.set({text: "test"})
          );
        });
    });
})
```

## テストの実行
テストが実行され成功すれば以下のようになります。

```console
$ firebase serve --only firestore
$ npm run jest
...
 PASS  __tests__/index.test.jst.js
 PASS  __tests__/firestore.test.js

Test Suites: 2 passed, 2 total
Tests:       3 passed, 3 total
Snapshots:   0 total
Time:        2.002s
Ran all test suites.
```

## 補足
以下コマンドでエミュレーターを起動しないとfirestoreのテストができないので注意

```console
$ firebase serve --only firestore
```
jest実行時に起動するようにすると起動忘れがないかも

```package.json
"test": "firebase serve --only firestore & jest"
```

jestの細かな設定をしないのであれば以下コマンドもしなくていいかも

```console
$ jest --init
```