How to Validate Google Firebase Apps with Automated Tests
Use Firebase Local Emulator Suite to test app behavior and Security Rules safely, then run the same repeatable checks locally and in CI.
Validate a Firebase app by running its relevant services in the Firebase Local Emulator Suite, connecting the app or test code to those emulators, and automating checks for service behavior and Security Rules. Use the same project ID in the Firebase CLI and app configuration, and prefer a demo- project ID for automated tests. A real project can still reach live Firebase services for products whose emulators are not running.
The suite is for local development, integration testing, and QA. It is designed for accuracy, not production performance or security; do not treat its emulators as self-hosted production services. See Google’s Local Emulator Suite overview.
1. Decide what your tests must validate
List the Firebase products used by the app and map them to critical user or system flows. Start with the narrowest useful set of emulators, then run the emulators together when a flow crosses services.
| Concern | Emulator or test path | Example assertion |
|---|---|---|
| Authentication | Authentication emulator | A user can sign in, and the app handles an invalid credential or signed-out state. |
| Database access | Firestore or Realtime Database emulator | A client can read or write the documents allowed by the app’s rules. |
| Storage access | Storage emulator, where supported for your workflow | A user can upload an allowed object and is denied an unauthorized path. |
| Backend behavior | Functions emulator with the relevant database or Auth emulator | A database event triggers the expected function behavior. |
| Deployment behavior | Hosting or App Hosting emulator as appropriate | Local app routes or deployment configuration behave as expected. |
Firebase supports combinations of emulators, including Authentication, Firestore, Realtime Database, Storage, Hosting, and Functions. Check the current docs for product support and preview labels before building a test dependency on a preview feature.
2. Configure a safe emulator project
Install and initialize the Firebase CLI for the repository, choose the products needed by the tests, and configure their ports. Keep the project ID consistent in .firebaserc, the CLI invocation, and the app or test configuration. This matters especially for cross-service behavior such as an authenticated database request or a Firestore write that triggers a function.
For automated tests, use a demo project ID such as demo-firebase-tests when possible. A demo project has no live Firebase resources. If code calls a Firebase product without its emulator running, the demo setup fails instead of silently reaching a live service. With a real project ID, any service without a running emulator may still connect to live resources, which can change data, consume usage, or incur charges. Firebase recommends demo projects wherever possible. See CLI setup and configuration and Firestore emulator connection and project safety.
{
"emulators": {
"auth": { "port": 9099 },
"firestore": { "port": 8080 },
"functions": { "port": 5001 },
"ui": { "enabled": true, "port": 4000 }
}
}
This is an illustrative firebase.json fragment. Add only emulators the app needs and retain any other existing project configuration. Common current defaults include Auth 9099, Firestore 8080, Functions 5001, Emulator UI 4000, Realtime Database 9000, Storage 9199, Hosting 5000, and Pub/Sub 8085. Defaults and supported products can change; check the current install guide and make local and CI port configuration agree.
{
"projects": {
"default": "demo-firebase-tests"
}
}
Use your actual selected project ID in both this file and app/test initialization. Do not rely on a demo ID in one process and a real project ID in another.
3. Connect the app or test code
Point the SDK instances used by the test at the local emulator before making requests. Emulator hostnames differ by runtime: a browser on the development machine can often use 127.0.0.1; Android emulators commonly need 10.0.2.2 to reach the host machine. A container or remote CI worker may need a service hostname or forwarded port. Do not assume localhost has the same meaning in every environment.
Web SDK example
import { initializeApp } from "firebase/app";
import { getAuth, connectAuthEmulator } from "firebase/auth";
import { getFirestore, connectFirestoreEmulator } from "firebase/firestore";
const app = initializeApp({
apiKey: "demo-api-key",
authDomain: "demo-firebase-tests.firebaseapp.com",
projectId: "demo-firebase-tests"
});
const auth = getAuth(app);
const db = getFirestore(app);
if (import.meta.env.MODE === "test" || import.meta.env.DEV) {
connectAuthEmulator(auth, "http://127.0.0.1:9099");
connectFirestoreEmulator(db, "127.0.0.1", 8080);
}
Initialize the emulator connection once for each SDK instance and do so before the first operation. Use a test-specific app configuration or an explicit environment switch so a test build cannot accidentally point at production. Firebase’s Auth connection guide documents the Auth connection method; consult the relevant SDK guide for other platforms.
Android example
// Call during test or debug setup, before Auth/Firestore operations.
FirebaseAuth.getInstance().useEmulator("10.0.2.2", 9099);
FirebaseFirestore.getInstance().useEmulator("10.0.2.2", 8080);
The Android emulator’s 10.0.2.2 host address is specific to reaching the development machine from that emulator. For a physical device, container, or CI environment, use a reachable host and ensure the port is exposed. Firebase documents platform-specific connection methods in its connect and prototype guide.
4. Test service behavior and Security Rules
Cover both success and denial paths. For each important operation, test the relevant identity state (signed out, ordinary user, owner, or privileged role), allowed data, denied data, and expected side effects. Keep assertions about application behavior distinct from assertions about access control so a failure points to the right layer.
Firestore Rules test example (JavaScript)
Use the Rules Unit Testing library with the Firestore emulator to test client-style access. The following is a compact example; adapt the rule path and expected policy to the app.
import { readFileSync } from "node:fs";
import {
initializeTestEnvironment,
assertSucceeds,
assertFails
} from "@firebase/rules-unit-testing";
import { doc, getDoc, setDoc } from "firebase/firestore";
const projectId = "demo-firebase-tests";
const testEnv = await initializeTestEnvironment({
projectId,
firestore: {
rules: readFileSync("firestore.rules", "utf8")
}
});
try {
await testEnv.clearFirestore();
const alice = testEnv.authenticatedContext("alice").firestore();
const bob = testEnv.authenticatedContext("bob").firestore();
const signedOut = testEnv.unauthenticatedContext().firestore();
await assertSucceeds(setDoc(doc(alice, "users/alice"), { name: "Alice" }));
await assertFails(setDoc(doc(bob, "users/alice"), { name: "Changed by Bob" }));
await assertFails(getDoc(doc(signedOut, "users/alice")));
} finally {
await testEnv.cleanup();
}
Run Rules tests against the emulator and include negative cases: a request should fail when the user, document path, or submitted fields violate policy. Rules tests only prove the client-side access path you exercised. Firestore server client libraries bypass Firestore Security Rules and authenticate with Google Application Default Credentials, so a test using a server library cannot prove that client requests are properly allowed or denied. It can still test trusted server logic or seed data. See Google’s Rules testing guide.
Authentication and Functions flows
The Auth emulator supports account creation and management, including email/password, phone and SMS, SMS multi-factor, third-party identity providers such as Google, and custom token authentication. When the related emulators are running, Auth interactions with Functions and Firestore or Realtime Database Rules can be prototyped without additional setup. A combined flow should use one project ID across the CLI, app, and test harness.
The Functions emulator can run HTTPS, callable, task queue, and supported background functions. Trigger background events through the Emulator UI or app/test code, then assert the observable outcome. Integrations that call external Firebase or Google APIs can require additional setup; running Functions locally does not mean every external dependency is emulated. See Run functions locally.
5. Make test data repeatable
A test that passes only after a particular manual setup is not a reliable automated check. Clear state before each suite or load a known baseline, and ensure tests do not depend on execution order. Firestore emulator documentation describes a reset endpoint and import/export options for reusable data sets. The example above uses the Rules test environment’s clear method; for other harnesses, use the reset or import/export approach documented for the emulator in use. See Firestore emulator data reset and import/export.
- Choose whether each test starts empty or from a named baseline.
- Reset state during setup, not after an assertion has already depended on it.
- Use unique user IDs or document paths if tests run concurrently.
- Keep emulator data and project IDs isolated between parallel CI jobs.
- Do not use production data as an unexamined emulator fixture; remove sensitive data and identifiers.
6. Run the same checks locally and in CI
firebase emulators:exec starts the selected emulators, runs a command, and shuts them down afterward. Put the normal test command there so developers and CI use the same lifecycle.
firebase emulators:exec --project demo-firebase-tests --only auth,firestore,functions "npm test"
For example, a package script can wrap the exact same command used in CI:
{
"scripts": {
"test:firebase": "firebase emulators:exec --project demo-firebase-tests --only auth,firestore,functions \"npm test\""
}
}
Adjust the emulator list and test runner to the app. The Firebase workflow guide also documents using emulators:exec with a shell test script; see Connect your app and automate tests. In CI, install the intended Firebase CLI version, expose the configured ports to the test process, use the same demo project ID, and preserve emulator logs when a job fails.
7. Performance, reliability, and cost
Emulator tests avoid depending on a live Firebase project’s state for the emulated services, making them useful for repeatable development and CI checks. They do not establish production latency, capacity, production security, or the behavior of external services. Use separate checks for deployment and production configuration when those claims matter.
Keep the suite fast by starting only the services needed for a test group, avoiding needless cross-service setup, and resetting data with a deliberate baseline. Reliability depends on aligned project IDs, reachable ports, deterministic fixtures, and cleanup. Firebase’s docs do not provide a universal performance or cost benchmark for a particular app; actual runtime depends on the selected emulators, test volume, machine, and test code. Local emulator execution is not a substitute for verifying external API costs or production billing behavior.
8. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Requests appear to reach a real Firebase project | A real project ID is configured and the requested product emulator is not running or the SDK was not connected. | Use a demo- project for tests, start the matching emulator, and verify the app’s runtime configuration and project ID. |
| Connection refused or emulator host cannot be reached | Wrong host for the runtime, emulator not started, port mismatch, or CI/container port not exposed. | Check the configured and actual port; use the host reachable from that runtime. Android emulators commonly use 10.0.2.2 for the host machine. |
| Cross-service trigger does not run | CLI, app, and test code use different project IDs, or the relevant emulator is not started. | Align project IDs and include all participating emulators in the command. |
| Rules test unexpectedly succeeds or fails | The test uses a server client, auth context is wrong, or rules/data do not match the intended case. | Exercise the client SDK path with authenticated or unauthenticated test contexts and assert both allowed and denied requests. |
| Tests pass alone but fail as a suite | State leaks across tests, tests share IDs, or order affects fixtures. | Clear or import a known baseline, isolate IDs, and remove ordering dependencies. |
| Function test calls an unexpected external service | The function depends on an external Firebase or Google API that is outside the configured emulators. | Identify and explicitly configure or isolate that integration; do not assume the emulator suite replaces every external API. |
| Emulator UI or service port conflicts | Another process uses a default port or local and CI configurations differ. | Set explicit ports in firebase.json, update client connection settings, and keep CI port mapping in sync. |
Or skip the browser setup
For screenshots of your app’s test pages, ScreenshotNeo provides a one-call website screenshot API and MCP server. The code below captures a URL as WebP; see the ScreenshotNeo API documentation for request options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
- Cookie banners, newsletter popups, and chat widgets are removed before the shot; each cleanup step can be turned off.
- Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed; response headers report the page verdict and billing status.
- An MCP server lets AI agents use screenshot, page information, and PDF capture tools.
- 1,000 screenshots a month are free with no card; paid plans start at $5 for 3,000 screenshots.
Create a free ScreenshotNeo account to get 1,000 screenshots a month with no card.
FAQ
Can emulator tests replace production checks?
No. They validate local service behavior and integration paths, not production performance, security, deployment configuration, or every external dependency.
Should I run every emulator for every test?
No. Run the emulators required by the flow. Add multiple services when a test specifically covers their interaction.
Can a server-side Firestore test validate Security Rules?
No. Firestore server client libraries bypass those rules. Use a client-side SDK test against the Firestore emulator for rule decisions.
Can I use a real Firebase project ID with the emulators?
It is possible, but products without a running emulator may still reach live resources. A demo project avoids that risk for automated tests.
Where should I check emulator ports and preview support?
Check Firebase’s current install and configuration guide and the selected product’s emulator documentation before publishing or updating the test setup.


