Skip to content

AI & TechnologyHow to Test Firebase Security Rules with the Emulator (2026)

Simon Dziak
Simon Dziak
Founder & developer, App369
LinkedIn
Published October 12, 2026 · Updated October 21, 2026Updated October 21, 2026Facts last verified October 21, 20267 min

Disclosure: App369 publishes this guide and sells a fixed-price Firebase security rules audit.

Key takeaways

  • Test Firebase Security Rules by trying to break them: another user's data, another tenant's data, and a user changing their own role. Write those attempts as unit tests so they run on every rules change.
  • With @firebase/rules-unit-testing you create a test environment with initializeTestEnvironment, act as users with authenticatedContext and unauthenticatedContext, and check results with assertSucceeds and assertFails.
  • Seed test data inside withSecurityRulesDisabled, otherwise your own rules block the setup. Rules are skipped only inside that callback.
  • Use a demo- project ID so test code cannot reach production resources. The Firestore emulator clears its data on shutdown, so each run starts clean.
  • firebase emulators:exec starts the emulators, runs your test command and shuts them down, which suits CI. Make the rules tests a required check on any pull request that touches rules files.
  • The Emulator UI Requests tab shows how each request was evaluated against your rules, so you can see which rule allowed or denied it.

Pricing an app? Try the free app cost calculator or get a range for your project in the instant quote.

The only reliable way to know your Firebase Security Rules work is to try to break them, automatically, every time they change. Reading rules and clicking around the app misses the cases that matter: another user’s data, another tenant’s data, and a user giving themselves a higher role. The Firebase Emulator Suite and the @firebase/rules-unit-testing library let you write those attempts as ordinary unit tests that run in seconds and never touch production.

This is the technique behind App369’s fixed-price Firebase Security Rules Audit, and the kind of test Simon relies on for the rules in Workspace369, his production Flutter and Firebase SaaS. If you want the rules themselves explained first, start with the Firebase Security Rules guide.

Step 1: Install and start the emulators

You need Node.js, a recent Firebase CLI and a Java runtime for the Firestore emulator. The install and configure docs list the minimum versions, but they can lag behind the CLI, so if the emulator refuses to start, install a current long-term-support JDK and read the CLI’s error message. Then, in your project:

firebase init emulators
firebase emulators:start --only firestore

firebase init emulators lets you pick emulators and downloads their binaries. By default the Firestore emulator listens on port 8080 and the Emulator UI on port 4000. Make sure firebase.json points at your rules file, for example "firestore": { "rules": "firestore.rules" }, plus an emulators block if you want non-default ports. The Firestore emulator docs also recommend demo project IDs (prefixed demo-): code can’t accidentally reach production resources, and no real Firebase project is needed.

Step 2: Set up the test library

npm install --save-dev @firebase/rules-unit-testing firebase

The current library (6.x at the time of writing) is a peer of the modular Firebase JS SDK: version 6 needs firebase 13 and Node.js 24.12 or newer, while version 5 works with firebase 12 and Node.js 20 or newer. Match the two, or npm warns about the peer dependency. You test through RulesTestEnvironment, created with initializeTestEnvironment. The official unit-test guide documents these pieces:

  • initializeTestEnvironment({ projectId, firestore: { rules } }) creates the environment from your rules text.
  • testEnv.authenticatedContext(uid, tokenOptions) acts as a signed-in user; testEnv.unauthenticatedContext() acts as a signed-out client.
  • testEnv.withSecurityRulesDisabled(callback) runs setup code that bypasses rules, so you can seed data.
  • assertSucceeds(promise) and assertFails(promise) check whether rules allowed or denied an operation.
  • testEnv.clearFirestore() clears data between tests, and testEnv.cleanup() releases resources when you finish.

Step 3: Write a first test file

Say your rules let a user read their own profile, create it only as a plain member, and never change their own role:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{uid} {
      allow read: if request.auth != null && request.auth.uid == uid;
      allow create: if request.auth != null && request.auth.uid == uid
        && request.resource.data.role == 'member';
      allow update: if request.auth != null && request.auth.uid == uid
        && request.resource.data.role == resource.data.role;
    }
  }
}

A Jest-style test file in TypeScript:

import { readFileSync } from 'fs';
import {
  initializeTestEnvironment,
  assertFails,
  assertSucceeds,
  RulesTestEnvironment,
} from '@firebase/rules-unit-testing';
import { doc, getDoc, setDoc, updateDoc } from 'firebase/firestore';

let testEnv: RulesTestEnvironment;

beforeAll(async () => {
  testEnv = await initializeTestEnvironment({
    projectId: 'demo-rules-test',
    firestore: {
      rules: readFileSync('firestore.rules', 'utf8'),
      host: '127.0.0.1',
      port: 8080,
    },
  });
});

afterAll(async () => {
  await testEnv.cleanup();
});

beforeEach(async () => {
  await testEnv.clearFirestore();
  // Seed data with rules switched off
  await testEnv.withSecurityRulesDisabled(async (ctx) => {
    await setDoc(doc(ctx.firestore(), 'users/alice'), { role: 'member' });
  });
});

test('a user can read their own profile', async () => {
  const alice = testEnv.authenticatedContext('alice');
  await assertSucceeds(getDoc(doc(alice.firestore(), 'users/alice')));
});

test('a user cannot read someone else\'s profile', async () => {
  const bob = testEnv.authenticatedContext('bob');
  await assertFails(getDoc(doc(bob.firestore(), 'users/alice')));
});

test('a signed-out client cannot read anything', async () => {
  const anon = testEnv.unauthenticatedContext();
  await assertFails(getDoc(doc(anon.firestore(), 'users/alice')));
});

test('a user cannot promote themselves to admin', async () => {
  const alice = testEnv.authenticatedContext('alice');
  await assertFails(updateDoc(doc(alice.firestore(), 'users/alice'), { role: 'admin' }));
});

Run it while the emulator is up, with your usual test runner. The seed step matters: tests that depend on existing documents must create them with withSecurityRulesDisabled, otherwise your own rules block the setup.

Step 4: Decide what to test

Passing tests for the happy path prove little. Write the attacks:

  1. Cross-user reads and writes. User B reading, updating and deleting user A’s documents, in every collection that holds user data.
  2. Cross-tenant access. If you have workspaces, teams or organizations, a member of tenant 1 must fail to read or write tenant 2’s documents, including sub-collections. Seed two tenants and test both directions.
  3. Role escalation. A user writing their own role, isAdmin or plan field on create and on update. Also test adding themselves to another tenant’s member list.
  4. Field-level validation. Creates with missing or extra fields, wrong types, oversized strings, and fields the client should never set, such as server-owned timestamps or counters.
  5. Signed-out access. Every path with unauthenticatedContext().
  6. Storage paths. The same user and tenant checks against Storage rules. Pass storage: { rules } to initializeTestEnvironment and use ctx.storage() in the tests.
  7. Queries, not just single reads. A rule that allows reading one document can still reject a list query that isn’t constrained to what the rule permits. Test the exact query your app runs.

For each tenant-isolation rule, write one test that should succeed and at least one that should fail. If you can’t make a failing test fail, you have found a hole.

Step 5: Run it in CI

firebase emulators:exec starts the emulators, runs your command, and shuts them down when the command ends. The emulator docs describe it as the way to run scripted tests, and it suits continuous integration:

firebase emulators:exec --only firestore "npm test"

The Firestore emulator clears its data when it shuts down, so every CI run starts clean. Cache the emulator download (the CLI keeps the emulator JARs in ~/.cache/firebase/emulators/) between CI runs if your provider supports it, and add the Java runtime to the CI image. Make the rules test a required check for any pull request that touches firestore.rules, storage.rules or the tests.

Step 6: Debug with the request monitor

When a test fails and you don’t know which rule caused it, run firebase emulators:start, point a manual client at it, and open the Emulator UI at http://localhost:4000. In the Firestore section, the Requests tab shows each request and traces how your rules were evaluated, which line allowed or denied it. The Firestore emulator docs describe this request monitoring. It is the fastest way to find out that a rule you thought applied never matched.

When to get help

Before you write tests, a quick first pass helps: paste your rules into the free Firebase Security Rules checker to spot public paths, test-mode rules and sign-in-only checks. It runs in your browser.

If you don’t have the time to build the model of who should access what, or you have already found a hole and want the fixes reviewed, App369’s fixed-price Firebase Security Rules Audit + Fix PR is $950 and delivered in 5 business days. It covers Firestore and Storage rules plus up to 15 Cloud Functions in one Firebase project, and you get a written role and tenant access model, an emulator test suite for cross-user, cross-tenant and role-escalation attempts, a severity-ranked report and a pull request with fixes that passes the suite. It runs on the emulator or test data only, and it is a technical review, not a compliance certificate.

Frequently asked questions

How do I test Firestore security rules locally?
Run the Firestore emulator with firebase emulators:start --only firestore, then write tests with @firebase/rules-unit-testing. Create a test environment with initializeTestEnvironment, act as users with authenticatedContext, and check results with assertSucceeds and assertFails.
Can I test Firebase Storage rules the same way?
Yes. Pass a storage option with your rules to initializeTestEnvironment, start the Storage emulator, and perform uploads and downloads through the context's storage() client. The same assertions apply.
How do I run Firebase rules tests in CI?
Use firebase emulators:exec to start the emulators, run your test command and stop them, for example firebase emulators:exec --only firestore "npm test". The CI machine needs Node.js, the Firebase CLI and a Java runtime. Firebase calls emulators:exec generally more appropriate for CI workflows.
How do I add test data when my rules would block it?
Call testEnv.withSecurityRulesDisabled with an async callback and write documents through the context's firestore() client. Rules are skipped inside that callback only.
Does passing rules tests mean my app is secure?
No. Tests show your rules behave as you described them, so they are only as good as the cases you wrote. They do not cover Cloud Functions logic, leaked credentials or anything outside Firebase.
Which version of @firebase/rules-unit-testing should I install?
Match it to your firebase package. As of October 2026, version 6 needs firebase 13 and Node.js 24.12 or newer, and version 5 works with firebase 12 and Node.js 20 or newer. Check the peer dependency warning from npm.

Sources

  1. Firebase docs, Build unit tests for Security Rules
  2. Firebase docs, Install, configure and integrate the Local Emulator Suite
  3. Firebase docs, Connect your app to the Cloud Firestore Emulator
  4. Firebase docs, Connect your app to the Cloud Storage emulator
  5. Firebase docs, Test your Cloud Firestore Security Rules
  6. Firebase docs, Prototype and test (emulators:exec)
Tags
#firebase security rules testing #firestore rules unit test #firebase emulator #rules-unit-testing #firebase emulators exec #firestore security #storage rules
Share:

Related Resources

Related guides