You have unlimited access as a PRO member
You are receiving a free preview of 3 lessons
Your free preview as expired - please upgrade to PRO
Contents
Recent Posts
- Object Oriented Programming With TypeScript
- Angular Elements Advanced Techniques
- TypeScript - the Basics
- The Real State of JavaScript 2018
- Cloud Scheduler for Firebase Functions
- Testing Firestore Security Rules With the Emulator
- How to Use Git and Github
- Infinite Virtual Scroll With the Angular CDK
- Build a Group Chat With Firestore
- Async Await Pro Tips
Testing Firestore Security Rules With the Emulator
Episode 147 written by Jeff DelaneySource code for Testing Firestore Security Rules With the Emulator on Github
The most exciting announcement (in my opinion) at Firebase Summit 2018 was the local emulator for Firestore and RTDB, making it possible to test security rules locally or on a CI server. If you’re brand new to Firebase security, make sure to watch the Firestore Security Rules Guide.
Project Setup
The following lesson creates an isolated testing environment solely for testing rules logic. It does not need to be directly coupled your frontend code, which is a useful if your database spans across multiple frontend clients (ie Web, iOS, and Android).
To keep our code as simple as possible, this lesson uses Jest with vanilla JS.
mkdir rules && cd rules |
Now that we have a testing environment in place, let’s link our project and opt-in to the emulator.
firebase init firestore |
You must have Java installed on your local machine to work with the emulator
Testing Helpers
Like most test suites, it’s useful to start with a few helpers to keep the code succinct and readable. Below are several helpers I have developed for working with the emulator in Jest.
Setup the Database with Auth and Mock Data
The setup method is the most important helper. It initializes the database with a unique projectId, then optionally seeds it with a mock user and mock data.
const firebase = require('@firebase/testing'); |
Usage of the setup method looks like this:
const mockUser = { |
Teardown
It’s a good practice to delete your app instances after the test run, otherwise Jest might complain about hanging async operations.
module.exports.teardown = async () => { |
Custom Async Jest Matchers
As a final touch, we will also implement custom matchers improve readability. Every Firestore rule test will be verifying that an operation was either (1) allowed or (2) denied. The matchers below make it possible to write tests that look like expect(read).toAllow()
. This part is optional, but will make life easier if you have a large test suite.
expect.extend({ |
Testing Firestore Rules
Let’s now take a look at several practical testing scenarios.
Ensure Rules are Secure by Default
First, let validate that database rules are secure by default - meaning a read to any random collection should be denied.
service cloud.firestore { |
Below I offer several different ways to write your rules, both with the built-in Firebase testing helpers and our custom Jest matchers.
const { setup, teardown } = require('./helpers'); |
Seeding the Database with Mock Data
Many rules require checks on related documents, for example, checking if a user has a specific access role before they can read/write to a collection. Rules like this require mock data to be present in the database before the rules are evaluated. Let’s write a test that requires an authenticated user and that seeds Firestore with mock data .
Let’s imagine we have a project management app that allows read/write for admin users OR users contained in an members access control list.
// Role-based authorization |
Notice how each test in this suite initializes a fresh database instance with a different authenticated user, but maintains the same mock data state.
const { setup, teardown } = require('./helpers'); |
The End
The Firestore emulator will deliver a huge boost of confidence to your backend security logic and prevent regressions that lead to catastrophic data breaches. Although the emulator is still in beta, it has already become an important tool in my workflow and I’m looking forward to experimenting with other use-cases.