@firebase/testingをやめて@firebase/rules-unit-testingを使おう


2021/10/04追記: rules-unit-testingのバージョンがアップして使い方が変わりました。紹介記事を書いているのでよければこちらもご覧ください。
https://zenn.dev/moga/articles/firebase-rules-unit-testing-v2

---追記終わり

2020年8月20日にFirebase SDKの7.19.0がリリースされました。このバージョンではTestingSDKに以下のアップデートが入りました。

@firebase/testingのいわば後継として、新たに@firebase/rules-unit-testingがリリースされました。名前が変更されて、何のためのパッケージなのかわかりやすくなりました。対応PRは、引用RTでmonoさんが教えてくださったこちらになります。

軽く目を通してみると、このリリースに至った経緯はGooglerしか見ることができませんでした。しかし、破壊的変更が発生するにも関わらずこのリリースを行ったことを考えると、今後はこの新しいTestingSDKを使っていくほうがよいでしょう。

違い

ReleaseNoteに記載されているとおりですが、以下の違いがあります。

  • インターフェースは互換性がある
  • assertFails()の挙動が変わる
    • いままではassertFailsの引数に渡したPromiseがrejectされればfailとしていたようだが、今回からFirestoreのrulesに弾かれたときに発生するPERMISSION_DENIEDのときのみfailとするようになる。これにより、より正確なrulesのテストになる。
  • initializeAdminApp()firebase-admin依存になる
    • 今まではfirebase-adminを模倣したインスタンスを返していたが、新しいパッケージではfirebase-adminを使ってインスタンスを返すようになる

対応

@firebase/rules-unit-testingをインストールします。すでに@firebase/testingを使っている場合は、不要になるのでアンインストールしておきましょう。

$ npm uninstall @firebase/testing
$ npm i -D @firebase/rules-unit-testing

import文を書き換えます。

- import * as firebase from '@firebase/testing'
+ import * as firebase from '@firebase/rules-unit-testing'

書き換えが完了したら一度テストを実行してみるとよいでしょう。テストの書き方によってはいくつかFAILしているかもしれません。それは、setメソッドでTimestampFieldValueinvalid objectとかinvalid dataというエラーの場合は下記が原因です。

client用Firestoreとadmin用Firestoreは別物

下記のようにする必要があります。

import * as firebase from '@firebase/rules-unit-testing'
import * as admin from 'firebase-admin'

const projectId = 'sample'
const databaseName = 'test'

const clientFirestore = firebase.initializeTestApp({ projectId, databaseName }).firestore()
const adminFirestore  = firebase.initializeAdminApp({ projectId, databaseName }).firestore()
const clientUserRef = clientFirestore.collection('users').doc()
const adminUserRef  = adminFirestore.collection('users').doc()

// OK: Client用のFirestoreには@firebase/rules-unit-testingから生成したFieldValueを渡さなければならない
clientUserRef.set({ createdAt: firebase.firestore.FieldValue.serverTimestamp() })

// NG: Client用のFirestoreにfirebase-adminから生成したFieldValueを渡すとエラーになる
clientUserRef.set({ createdAt: admin.firestore.FieldValue.serverTimestamp() })

// OK: Admin用のFirestoreにはfirebase-adminから生成したFieldValueを渡さなければならない
adminUserRef.set({ createdAt: admin.firestore.FieldValue.serverTimestamp() })

// NG: Admin用のFirestoreに@firebase/rules-unit-testingから生成したFieldValueを渡すとエラーになる
adminUserRef.set({ createdAt: firebase.firestore.FieldValue.serverTimestamp() })

ようするにFirestoreに渡す値はパッケージが一致している必要があるということです。これが一致していないとエラーになります。実際、私が担当しているプロダクトのTestingSDKを差し替えるとこれに起因するエラーがいくつか発生していました。(いままでは、逆に、admin用のFirestoreにfirebase-adminから生成したTimestampを渡すとエラーになっていたと思う)

おわりに

何か間違っている点などありましたらコメントいただけると幸いです。また、Firebaseに関する発信をTwitterYoutubeでおこなっているので、よければ見てください。

追記

この記事を書いたことがきっかけで、実際に@firebase/rules-unit-testingを試してくれた人がいて、そのおかげでfirebase-js-sdkにコントリビューターになれました!SDKで怪しい挙動があったらGithubに行ってコードを読んでみるといいかもしれません。