Help us understand the problem. What is going on with this article?

Firestoreのruleをテストする

firestoreのruleのテストは重要なのでメモ。

準備

作業場所の準備

作業ディレクトリを作ってfirebase init firestoreを実行。

mkdir rules-test
cd rules-test
firebase init firestore

私は既存プロジェクトを使うことが多いですが、テストなので新規でもなんでも。

❯ Use an existing project
  Create a new project
  Add Firebase to an existing Google Cloud Platform project
  Don't set up a default project

ルールやIndexを設定するファイル名を聞いてくるので、基本デフォでOK。

? What file should be used for Firestore Rules? firestore.rules
? What file should be used for Firestore indexes? firestore.indexes.json

テストに必要なモジュールのインストール

必要なモジュールをインストール。
テストツールとしてjest, firebaseのテストライブラリとしてfirebase/testing、設定ファイルを読み込むためfsを導入。

npm init -f
npm install -D jest @firebase/testing fs

package.jsonの編集

npm testでjestが実行されるようにしておきます。

package.json
.
  "scripts": {
    "test": "jest"
  },
.

エミュレータのセットアップ

これがないとローカルでテストできません。まだsetup(ダウンロード)してないなら落とす。

firebase setup:emulators:firestore

エミュレータの起動

これはテスト実行直前でもいいです。

firebase serve --only firestore

テストの準備

jestは標準で*.test.js, *.spec.jsやtestディレクトリ下のファイルをテストとして認識するので、今回はtestを作り、その下にfirebase.test.jsを置いてみます。

mkdir __test__
touch __test__/firestore.test.js

テストの実装

ルール

とりあえず認証してないとread, writeできないようにしています。

firestore.rules
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write:if request.auth != null;
    }
  }
}

テスト

認証ユーザー情報を指定し、read, writeを実行。

firestore.test.js
const firebase = require('@firebase/testing');
const fs = require('fs');

const project_id = "my-project-id";

describe("Firestoreのテスト", () => {

    //実行前に一度だけ実行(初期化)
    beforeAll(
        async () => {
            await firebase.loadFirestoreRules({
                projectId: project_id,
                rules: fs.readFileSync('./firestore.rules', 'utf8'),
            });
        }
    );

    //ブロックが終わるたび実行
    afterEach(
        async () => {
            await firebase.clearFirestoreData({ projectId: project_id }); //データリセット
        }
    );

    //終わった後に一度だけ実行
    afterAll(
        async () => {
            await Promise.all(
                firebase.apps().map((app) => app.delete()) //生成したアプリを削除
            );
        }
    );

    //条件(projectIdとauth情報)をの指定を関数化
    //auth : {uid:'alice'}
    //auth : {uid:'alice', admin:true} admin
    //auth : null 未認証
    function authedApp(auth) {
        return firebase.initializeTestApp({
            projectId: project_id,
            auth: auth,
        }).firestore();
    }

    describe("messagesコレクションのルールテスト", () => {

        //読取りテスト
        test("messageの読取り", async () => {
            //条件(uidやprojectId)を指定してdbを生成
            const db = authedApp({ uid: 'alice' });
            //docRefを取得
            const message = db.collection("message").doc("alice");
            //取得ができるか
            await firebase.assertSucceeds(message.get());
        })

        //書き込みテスト
        test("messageの書き込み", async () => {
            //条件(uidやprojectId)を指定してdbを生成
            const db = authedApp({ uid: "alice" });
            //docRefを取得
            const message = db.collection("message").doc("alice");
            //書き込みができるか
            await firebase.assertSucceeds(
                message.set({ text: "hoge" })
            );
        })
    })

})

テスト実行

firestoreエミュレータを起動

別のコンソールを開いて、エミュレータを起動させておきます。

firebase serve --only firestore

テストの実行

テストを実行します。

npm test

下記のような感じでpassすればOKです。

> jest

 PASS  __test__/firestore.test.js
  Firestoreのテスト
    messagesコレクションのルールテスト
      ✓ messageの読取り (296ms)
      ✓ messageの書き込み (94ms)

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        1.706s, estimated 8s

参考

  • firestoreのテストについては概要はここらあたりを。
  • 詳しくはここらあたりを。
  • jestについてはここらあたりを。

少しだけ応用

テスト内容を一部変更

読取りテストの認証情報をnull(認証されてない状態)にしてテストを実行してみます。

テスト内容を変更

        //読取りテスト
        test("messageの読取り", async () => {
            //条件(uidやprojectId)を指定してdbを生成
+            const db = authedApp(null);
            //docRefを取得
            const message = db.collection("message").doc("alice");
            //取得ができるか
            await firebase.assertSucceeds(message.get());
        })

そうするとテストは失敗します。

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 passed, 2 total
Snapshots:   0 total
Time:        1.616s, estimated 2s
Ran all test suites.
npm ERR! Test failed.  See above for more details.

ルールを変更

readは認証なしでもOKのルールに変更してテストを実行してみます。

firebase.rules
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read;
      allow write:if request.auth != null;
    }
  }
}

正常にテストが通るはずです。

debug()関数の利用

認証情報をdebug()で囲んでやることにより、内容を出力することができます。
出力は、firestore-debug.logに出力されるようです。

firestore.rules
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read;
+      allow write:if debug(request.auth) != null;
    }
  }
}

auth情報まるまる出力してるのでややこしそうですが、認証情報としてaliceが使われているのがわかります。

map_value {
  fields {
    key: "uid"
    value {
      string_value: "alice"
    }
  }
  fields {
    key: "token"
    value {
      map_value {
        fields {
+          key: "uid"
+          value {
+            string_value: "alice"
+          }
        }
        fields {
          key: "iat"
          value {
            int_value: 0
          }
        }
        fields {
          key: "sub"
          value {
            string_value: "alice"
          }
        }
      }
    }
  }
}

テストのデプロイ

編集したルール(だけ)をデプロイしたい場合は、下記のようにします。

firebase deploy --only firestore:rules
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした