40
31

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Firestoreのruleをテストする

Last updated at Posted at 2019-11-24

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
40
31
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
40
31

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?