Menguji aturan keamanan
Saat membangun aplikasi, Anda mungkin ingin mengunci akses ke database Firestore Anda. Namun, sebelum melakukan peluncuran, Anda memerlukan Aturan Keamanan Firestore yang lebih mendetail. Dengan emulator Firestore, selain membuat prototipe dan menguji perilaku dan fitur umum aplikasi, Anda dapat menulis pengujian unit yang memeriksa perilaku Aturan Keamanan Firestore.
Panduan memulai
Untuk beberapa kasus pengujian dasar dengan aturan sederhana, cobalah contoh panduan memulai.
Memahami Aturan Keamanan Firestore
Terapkan Firebase Authentication dan Aturan Keamanan Firestore untuk autentikasi, otorisasi, dan validasi data tanpa server saat Anda menggunakan library klien seluler dan web.
Aturan Keamanan Firestore mencakup dua bagian:
- Pernyataan
match
yang mengidentifikasi dokumen dalam database Anda. - Ekspresi
allow
yang mengontrol akses ke dokumen tersebut.
Firebase Authentication memverifikasi kredensial pengguna dan memberikan landasan untuk sistem akses berbasis pengguna dan peran.
Sebelum membaca atau menulis data, setiap permintaan database dari library klien seluler/web Firestore dievaluasi berdasarkan aturan keamanan Anda. Apabila aturan tersebut menolak akses ke salah satu jalur dokumen yang ditentukan, seluruh permintaan akan gagal.
Pelajari Aturan Keamanan Firestore lebih lanjut di artikel Memulai Aturan Keamanan Firestore.
Menginstal emulator
Untuk menginstal emulator Firestore, gunakan Firebase CLI dan jalankan perintah di bawah ini:
firebase setup:emulators:firestore
Menjalankan emulator
Mulai dengan menginisialisasi project Firebase di direktori kerja Anda. Ini adalah langkah pertama yang umum saat menggunakan Firebase CLI.
firebase init
Mulai emulator menggunakan perintah berikut. Emulator akan berjalan hingga Anda menghentikan prosesnya:
firebase emulators:start --only firestore
Dalam sebagian besar kasus, sebaiknya Anda memulai emulator, menjalankan serangkaian pengujian, lalu menghentikan emulator setelah pengujian berjalan. Anda dapat melakukannya dengan mudah menggunakan perintah emulators:exec
:
firebase emulators:exec --only firestore "./my-test-script.sh"
Ketika dimulai, emulator akan mencoba berjalan pada port default (8080). Anda dapat mengubah port emulator dengan mengubah bagian "emulators"
pada file firebase.json
Anda:
{ // ... "emulators": { "firestore": { "port": "YOUR_PORT" } } }
Sebelum menjalankan emulator
Sebelum Anda mulai menggunakan emulator, perhatikan hal-hal berikut:
- Pertama, emulator akan memuat aturan yang ditentukan dalam kolom
firestore.rules
filefirebase.json
Anda. Tutorial ini mengharapkan nama file lokal yang berisi Aturan Keamanan Firestore Anda dan menerapkan aturan tersebut ke semua project. Jika Anda tidak memberikan jalur file lokal atau menggunakan metodeloadFirestoreRules
seperti yang dijelaskan di bawah, emulator akan memperlakukan semua project dengan aturan terbuka. - Meskipun sebagian besar Firebase SDK bekerja dengan emulator secara langsung, hanya library
@firebase/rules-unit-testing
yang mendukung tiruanauth
di Aturan Keamanan, sehingga pengujian unit menjadi lebih mudah. Selain itu, library ini mendukung beberapa fitur khusus emulator seperti menghapus semua data, seperti yang tercantum di bawah ini. - Emulator juga akan menerima token Firebase Auth produksi yang disediakan melalui SDK Klien dan mengevaluasi aturan yang sesuai, agar aplikasi Anda dapat terhubung langsung ke emulator dalam pengujian manual dan integrasi.
Menjalankan pengujian unit lokal
Menjalankan pengujian unit lokal dengan JavaScript SDK v9
Firebase mendistribusikan library pengujian unit Aturan Keamanan dengan JavaScript SDK versi 9 dan SDK versi 8. API library sangat berbeda. Kami merekomendasikan library pengujian v9, yang lebih sederhana dan memerlukan lebih sedikit penyiapan untuk terhubung ke emulator sehingga dapat dengan aman menghindari penggunaan resource produksi secara tidak sengaja. Untuk kompatibilitas mundur, kami terus menyediakan library pengujian v8.
Gunakan modul @firebase/rules-unit-testing
untuk berinteraksi dengan emulator
yang berjalan secara lokal. Jika Anda menemukan error ECONNREFUSED
atau waktu tunggu,
periksa kembali apakah emulator sedang berjalan atau tidak.
Kami sangat menyarankan penggunaan Node.js versi terbaru agar Anda dapat menggunakan notasi async/await
. Hampir semua perilaku yang mungkin ingin Anda uji melibatkan fungsi asinkron, dan modul pengujian dirancang untuk digunakan dengan kode berbasis Promise.
Library Pengujian Unit Aturan v9 selalu mengetahui keberadaan emulator dan tidak pernah menyentuh resource produksi Anda.
Anda mengimpor library menggunakan pernyataan impor modular v9. Contoh:
import {
assertFails,
assertSucceeds,
initializeTestEnvironment
} from "@firebase/rules-unit-testing"
// Use `const { … } = require("@firebase/rules-unit-testing")` if imports are not supported
// Or we suggest `const testing = require("@firebase/rules-unit-testing")` if necessary.
Setelah diimpor, penerapan pengujian unit melibatkan:
- Membuat dan mengonfigurasi
RulesTestEnvironment
dengan panggilan keinitializeTestEnvironment
. - Menyiapkan data pengujian tanpa memicu Aturan, menggunakan metode praktis yang memungkinkan Anda mengabaikannya untuk sementara,
RulesTestEnvironment.withSecurityRulesDisabled
. - Menyiapkan rangkaian pengujian dan per-pengujian sebelum/setelah hook dengan panggilan untuk membersihkan data dan lingkungan pengujian, seperti
RulesTestEnvironment.cleanup()
atauRulesTestEnvironment.clearFirestore()
. - Menerapkan kasus pengujian yang meniru status autentikasi menggunakan
RulesTestEnvironment.authenticatedContext
danRulesTestEnvironment.unauthenticatedContext
.
Metode dan fungsi utilitas yang umum
Lihat juga metode pengujian khusus emulator di SDK v9.
initializeTestEnvironment() => RulesTestEnvironment
Fungsi ini menginisialisasi lingkungan pengujian untuk pengujian unit aturan. Panggil fungsi ini terlebih dahulu untuk penyiapan pengujian. Emulator harus berjalan agar eksekusi dapat berhasil dilakukan.
Fungsi ini menerima objek opsional yang menentukan TestEnvironmentConfig
, yang dapat terdiri dari project ID dan setelan konfigurasi emulator.
let testEnv = await initializeTestEnvironment({ projectId: "demo-project-1234", firestore: { rules: fs.readFileSync("firestore.rules", "utf8"), }, });
RulesTestEnvironment.authenticatedContext({ user_id: string, tokenOptions?: TokenOptions }) => RulesTestContext
Metode ini membuat RulesTestContext
, yang berperilaku seperti pengguna Authentication yang terautentikasi. Permintaan yang dibuat melalui konteks yang ditampilkan akan memiliki token Authentication tiruan yang dilampirkan. Secara opsional, teruskan objek yang menentukan klaim kustom atau penggantian untuk payload token Authentication.
Gunakan objek konteks pengujian yang ditampilkan dalam pengujian Anda untuk mengakses instance emulator yang dikonfigurasi, termasuk yang dikonfigurasi dengan initializeTestEnvironment
.
// Assuming a Firestore app and the Firestore emulator for this example import { setDoc } from "firebase/firestore"; const alice = testEnv.authenticatedContext("alice", { … }); // Use the Firestore instance associated with this context await assertSucceeds(setDoc(alice.firestore(), '/users/alice'), { ... });
RulesTestEnvironment.unauthenticatedContext() => RulesTestContext
Metode ini membuat RulesTestContext
, yang berperilaku seperti klien yang tidak login melalui Authentication. Permintaan yang dibuat melalui konteks yang ditampilkan tidak akan dilampiri token Firebase Auth.
Gunakan objek konteks pengujian yang ditampilkan dalam pengujian Anda untuk mengakses instance emulator yang dikonfigurasi, termasuk yang dikonfigurasi dengan initializeTestEnvironment
.
// Assuming a Cloud Storage app and the Storage emulator for this example import { getStorage, ref, deleteObject } from "firebase/storage"; const alice = testEnv.unauthenticatedContext(); // Use the Cloud Storage instance associated with this context const desertRef = ref(alice.storage(), 'images/desert.jpg'); await assertSucceeds(deleteObject(desertRef));
RulesTestEnvironment.withSecurityRulesDisabled()
Menjalankan fungsi penyiapan pengujian dengan konteks yang berperilaku seolah-olah Aturan Keamanan dinonaktifkan.
Metode ini menggunakan fungsi callback yang mengambil konteks pengabaian Aturan Keamanan dan menampilkan promise. Konteks akan dimusnahkan setelah promise me-resolve/menolak.
RulesTestEnvironment.cleanup()
Metode ini menghapus semua RulesTestContexts
yang dibuat di lingkungan pengujian dan membersihkan resource yang mendasarinya, sehingga memungkinkan keluar secara bersih.
Metode ini tidak mengubah status emulator dengan cara apa pun. Untuk mereset data di antara pengujian, gunakan metode penghapusan data khusus emulator aplikasi.
assertSucceeds(pr: Promise<any>)) => Promise<any>
Ini adalah fungsi utilitas kasus pengujian.
Fungsi ini menegaskan bahwa Promise yang disediakan dan menggabungkan operasi emulator akan diselesaikan tanpa pelanggaran Aturan Keamanan.
await assertSucceeds(setDoc(alice.firestore(), '/users/alice'), { ... });
assertFails(pr: Promise<any>)) => Promise<any>
Ini adalah fungsi utilitas kasus pengujian.
Fungsi ini menegaskan bahwa Promise yang disediakan dan menggabungkan operasi emulator akan ditolak dengan pelanggaran Aturan Keamanan.
await assertFails(setDoc(alice.firestore(), '/users/bob'), { ... });
Metode khusus emulator
Lihat juga metode pengujian dan fungsi utilitas umum di SDK v9.
RulesTestEnvironment.clearFirestore() => Promise<void>
Metode ini menghapus data dalam database Firestore yang termasuk dalam projectId
yang dikonfigurasi untuk emulator Firestore.
RulesTestContext.firestore(settings?: Firestore.FirestoreSettings) => Firestore;
Metode tersebut mendapatkan instance Firestore untuk konteks pengujian ini. Instance Firebase JS Client SDK yang ditampilkan dapat digunakan dengan API SDK klien (v9 modular atau v9 compat).
Memvisualisasikan evaluasi aturan
Emulator Firestore memungkinkan Anda memvisualisasikan permintaan klien di UI Emulator Suite, termasuk pelacakan evaluasi untuk Aturan Keamanan Firebase.
Buka tab Firestore > Requests guna melihat urutan evaluasi mendetail untuk setiap permintaan.
Menghasilkan laporan pengujian
Setelah menjalankan serangkaian pengujian, Anda dapat mengakses laporan cakupan pengujian yang menunjukkan evaluasi dari setiap aturan keamanan Anda.
Untuk mendapatkan laporan tersebut, buat kueri untuk endpoint yang terekspos pada emulator selagi emulator berjalan. Untuk versi yang mudah digunakan di browser, gunakan URL berikut:
http://localhost:8080/emulator/v1/projects/<project_id>:ruleCoverage.html
Proses ini memecah aturan Anda menjadi beberapa ekspresi dan subekspresi, dan Anda dapat mengarahkan mouse ke ekspresi/subekspresi tersebut untuk melihat informasinya lebih lanjut, termasuk jumlah evaluasi dan nilai yang ditampilkan. Untuk versi JSON mentah data ini, sertakan URL berikut dalam kueri Anda:
http://localhost:8080/emulator/v1/projects/<project_id>:ruleCoverage
Perbedaan antara emulator dan produksi
- Anda tidak harus membuat project Firestore secara eksplisit. Emulator secara otomatis membuat setiap instance yang diakses.
- Emulator Firestore tidak berfungsi dengan alur Firebase Authentication normal.
Sebagai gantinya, di Firebase Test SDK, kami telah menyediakan metode
initializeTestApp()
di libraryrules-unit-testing
, yang menggunakan kolomauth
. Handle Firebase yang dibuat menggunakan metode ini akan berperilaku seolah-olah telah berhasil diautentikasi sebagai entity apa pun yang Anda berikan. Jika Anda meneruskannull
, handle tersebut akan berperilaku sebagai pengguna yang tidak diautentikasi (misalnya, aturanauth != null
akan gagal).
Mengatasi masalah umum
Saat menggunakan emulator Firestore, Anda mungkin mengalami masalah umum berikut. Ikuti panduan berikut untuk memecahkan masalah perilaku tidak biasa yang Anda alami. Catatan ini ditulis dengan mempertimbangkan library pengujian unit Aturan Keamanan, tetapi pendekatan umumnya berlaku untuk Firebase SDK apa pun.
Perilaku pengujian tidak konsisten
Jika pengujian Anda sesekali berhasil dan sesekali gagal, bahkan tanpa perubahan apa pun pada pengujian itu sendiri, Anda mungkin perlu memastikan apakah pengujian tersebut telah diurutkan dengan benar atau belum.
Sebagian besar interaksi dengan emulator bersifat asinkron, jadi periksa kembali apakah semua kode asinkron telah diurutkan dengan benar. Anda dapat memperbaiki urutan tersebut dengan menggabungkan promise atau menggunakan banyak notasi await
.
Secara khusus, tinjau operasi asinkron berikut:
- Menyetel aturan keamanan, dengan, misalnya,
initializeTestEnvironment
. - Membaca dan menulis data, dengan, misalnya,
db.collection("users").doc("alice").get()
. - Pernyataan operasional, termasuk
assertSucceeds
danassertFails
.
Pengujian hanya berhasil saat pertama kali Anda memuat emulator
Emulator bersifat stateful. Emulator menyimpan semua data yang dituliskan kepadanya ke dalam memori, sehingga data apa pun akan hilang setiap kali emulator nonaktif. Jika Anda menjalankan beberapa pengujian terhadap project ID yang sama, setiap pengujian dapat menghasilkan data yang mungkin memengaruhi pengujian berikutnya. Anda dapat menggunakan salah satu metode berikut untuk mengabaikan perilaku ini:
- Menggunakan project ID unik untuk setiap pengujian. Perhatikan bahwa jika Anda memilih untuk melakukan hal ini, Anda harus memanggil
initializeTestEnvironment
sebagai bagian dari setiap pengujian; aturan hanya dimuat secara otomatis untuk project ID default. - Mengubah struktur pengujian Anda agar tidak berinteraksi dengan data yang ditulis sebelumnya (misalnya, gunakan koleksi yang berbeda untuk setiap pengujian).
- Menghapus semua data yang ditulis selama pengujian.
Penyiapan pengujian sangat rumit
Saat menyiapkan pengujian, Anda mungkin perlu mengubah data dengan cara yang tidak diizinkan oleh Aturan Keamanan Firestore. Jika aturan mempersulit penyiapan pengujian, coba gunakan RulesTestEnvironment.withSecurityRulesDisabled
dalam langkah penyiapan, sehingga operasi baca dan tulis tidak akan memicu error PERMISSION_DENIED
.
Setelah itu, pengujian Anda dapat menjalankan operasi sebagai pengguna terautentikasi atau tidak terautentikasi dengan menggunakan RulesTestEnvironment.authenticatedContext
dan unauthenticatedContext
. Dengan begitu, Anda dapat memvalidasi bahwa Aturan Keamanan Firestore mengizinkan / menolak kasus yang berbeda dengan benar.