If you're planning to unit test your Cloud Firestore security rules, you probably figured out you need to install the Firebase Emulator first. However, running your tests on a continuous integration environment might give you some headache.

So, let's go through some simple steps to test and deploy your Firestore security rules using the GitLab CI/CD runner.

For the sake of brevity, I'm assuming you're familiar with Firestore and have written your security rules. This guide will only teach how to automate your tests and deployment process.

Testing your Firestore rules

Before we start, you'll need to install the @firebase/testing package to test your security rules:



yarn add @firebase/testing --dev

Now let's start testing our rules! First, let's create our firestore.rules.spec.ts file (I'm using Typescript here but it's similar if you're using plain JS).

We need to import our @firebase/testing module and fs to read our security rules:



import * as firebase from ' @firebase/testing ' ; import * as fs from ' fs ' ;

Before we run our tests, let's load Firestore's security rules:



const projectId = ' my-firebase-project ' ; const rules = fs . readFileSync ( ' firestore.rules ' , ' utf8 ' ); beforeAll ( async ( done ) => { // Make your test app load your firestore rules await firebase . loadFirestoreRules ({ projectId , rules }); done (); });

Before each test case, we'll clear our Firestore data to avoid inconsistencies:



beforeEach ( async ( done ) => { // Reset our data from our test database await firebase . clearFirestoreData ({ projectId }); done (); });

We're going to test if only the author of a post can update it.



it ( ' allows the author to update a post ' , async ( done ) => { // Let's initialize the Admin SDK to populate some initial data const admin = firebase . initializeAdminApp ({ projectId }). firestore (); admin . doc ( ' posts/123 ' ). set ({ title : ' my post ' , authorId : ' leoDaVinci ' }); // Then, we start our test app passing the `authorId` as the logged in user. const auth = { uid : ' leoDaVinci ' }; const app = firebase . initializeTestApp ({ projectId , auth }). firestore (); const ref = app . doc ( ' posts/123 ' ); // Here we test if the `update` request succeeds. await firebase . assertSucceeds ( ref . update ({ title : ' updated post ' })); done (); });

Now, we're going to test if anonymous users can update posts:



it ( ' does not allow anonymous users to update a story ' , async ( done ) => { const admin = firebase . initializeAdminApp ({ projectId }). firestore (); admin . doc ( ' stories/123 ' ). set ({ name : ' my post ' , authorId : ' leoDaVinci ' }); // Here, we'll initialize the test app passing an `undefined` user const auth = undefined ; const app = firebase . initializeTestApp ({ projectId , auth }). firestore (); const ref = app . doc ( ' stories/123 ' ); // Our `update` request should fail because the user isn't the same as the `authorId` await firebase . assertFails ( ref . update ({ name : ' updated post ' })); done (); });

Running your tests locally

We're going to run our tests using Jest. Make sure you've installed it first:



yarn add jest @types-jest

Before we can run our tests, we need to install and start the Firebase Emulator:



# Install the emulator firebase setup:emulators:firestore # Start the emulator firebase serve --only firestore

Keep it running in the background, and run your tests:



jest

Deploying your Firestore rules using GitLab

However, that setup won't work out of the box when running your tests on a continuous integration environment like the GitLab CI/CD runner.

The Firebase Emulator requires Java. We can use a Docker container which already installs both Java and the Firebase Emulator for us.

Create a .gitlab-ci.yml file and use our custom Docker container:



image: wceolin/firebase-emulator stages: - test test: stage: test script: - yarn - firebase serve --only firestore - jest

However, we'll run into two problems:

When running firebase serve --only firestore , the emulator won't be recognized

, the emulator won't be recognized We can't run a serve job in parallel to running our tests

We can fix those issues by running the emulator's jar directly and using the start-server-and-test library to run both jobs together.

Let's start by adding a serve and test scripts to our package.json :



"scripts" : { "serve" : "java -jar $HOME/.cache/firebase/emulators/cloud-firestore-emulator-*.jar --host=127.0.0.1" , "test" : "jest" }

Now, let's run those scripts in your GitLab CI/CD environment:



image: wceolin/firebase-emulator stages: - test test: stage: test script: - yarn - start-server-and-test serve http-get://127.0.0.1:8080 test

Now, it will start the emulator from the jar file and run our test suite at the same time.

We can also automatically deploy it to Firebase by adding a new deploy stage to our GitLab config file:



image: wceolin/firebase-emulator stages: - test - deploy test: stage: test script: - yarn - start-server-and-test serve http-get://127.0.0.1:8080 test deploy: stage: deploy script: - firebase deploy --only firestore --token "$FIREBASE_TOKEN"

That's it! Now your Firestore project can be automatically and deployed. :)

PS. You have a look at this repository for a production app using this setup.