Testing rules with emulator in Firebase - Time & Space Complexity
When testing Firebase security rules with an emulator, we want to know how the time to run tests changes as we add more test cases.
How does the number of tests affect the total time spent checking rules?
Analyze the time complexity of running multiple rule tests using the Firebase emulator.
const { initializeTestEnvironment } = require('@firebase/rules-unit-testing');
async function runTests(n) {
const testEnv = await initializeTestEnvironment({ projectId: 'demo-project' });
for (let i = 0; i < n; i++) {
const db = testEnv.authenticatedContext('user' + i).firestore();
await db.collection('messages').add({ text: 'Hello ' + i });
}
await testEnv.cleanup();
}
This code runs n tests where each test tries to add a message to Firestore using the emulator.
Look at what repeats as n grows.
- Primary operation: Adding a document to Firestore via the emulator.
- How many times: Exactly n times, once per loop iteration.
Each test adds one document, so the total operations grow directly with n.
| Input Size (n) | Approx. Api Calls/Operations |
|---|---|
| 10 | 10 document adds |
| 100 | 100 document adds |
| 1000 | 1000 document adds |
Pattern observation: The number of operations grows in a straight line as n increases.
Time Complexity: O(n)
This means the time to run all tests grows directly in proportion to the number of tests.
[X] Wrong: "Running more tests won't affect total time much because the emulator is fast."
[OK] Correct: Each test still runs separately and takes time, so more tests add up linearly.
Understanding how test time grows helps you plan efficient testing and shows you think about scaling in real projects.
"What if we ran tests in parallel instead of one after another? How would the time complexity change?"
Practice
Solution
Step 1: Understand the role of the emulator
The Firebase emulator creates a safe environment that mimics Firestore without touching live data.Step 2: Identify the benefit of safe testing
This allows developers to test security rules without risking data loss or exposure in production.Final Answer:
To safely test rules without affecting live data -> Option AQuick Check:
Emulator = Safe testing environment [OK]
- Thinking emulator deploys rules to production
- Confusing emulator with data backup tool
- Assuming emulator speeds up production queries
Solution
Step 1: Recall the correct Firebase CLI command
The command to start emulators selectively uses 'firebase emulators:start --only' followed by the service name.Step 2: Match the command to Firestore emulator
For Firestore only, the command is 'firebase emulators:start --only firestore'.Final Answer:
firebase emulators:start --only firestore -> Option BQuick Check:
Correct CLI syntax = firebase emulators:start --only firestore [OK]
- Using 'firebase start firestore' which is invalid
- Confusing 'run' with 'start' in command
- Omitting '--only' flag
service cloud.firestore {
match /databases/{database}/documents {
match /messages/{msgId} {
allow read: if request.auth != null;
allow write: if false;
}
}
}
What will happen if an unauthenticated user tries to read a message?Solution
Step 1: Analyze the read rule condition
The rule allows read only if request.auth is not null, meaning user must be authenticated.Step 2: Check the user's authentication status
The user is unauthenticated, so request.auth is null, failing the condition.Final Answer:
Read is denied -> Option AQuick Check:
Unauthenticated read = denied [OK]
- Assuming read is allowed without authentication
- Confusing read and write permissions
- Ignoring the request.auth check
allow write: if request.auth.uid == userId;What is the likely error causing the failure?
Solution
Step 1: Check the rule variable usage
The rule compares request.auth.uid to userId, but userId is not defined or extracted from the document path or data.Step 2: Understand rule variable scope
Variables like userId must be defined in the path or request data; otherwise, the condition fails.Final Answer:
The variable 'userId' is not defined in the rule context -> Option DQuick Check:
Undefined variable causes rule failure [OK]
- Assuming emulator nullifies request.auth.uid
- Thinking '==' is invalid in rules
- Believing emulator ignores write rules
/users/{userId}. Which rule correctly enforces this and can be tested in the emulator?Solution
Step 1: Match userId from document path
In rules, userId is a variable from the path /users/{userId} and can be used in conditions.Step 2: Enforce write only if authenticated user matches userId
The condition 'request.auth.uid == userId' ensures only the user can write their own profile.Final Answer:
allow write: if request.auth.uid == userId; -> Option CQuick Check:
User writes own doc = request.auth.uid == userId [OK]
- Using '!=' instead of '==' causing wrong access
- Allowing any authenticated user to write all profiles
- Setting rule to always true, no security
Ready to prove it?
Pass the test and this pattern moves unknown
