unittest.mock.MagicMock. Both strategies are supported out of the box through the FirestoreDB API — no monkey-patching or third-party test helpers needed.
Two Testing Strategies
Emulator (Integration)
Runs all queries against a real Firestore protocol. Catches subtle query bugs, index requirements, and serialization edge cases.
MagicMock (Unit)
Replaces the client with a mock object. No network, no emulator, and instant test execution — ideal for testing business logic in isolation.
Integration Testing with the Firestore Emulator
The Google Cloud Firestore Emulator runs a full in-process Firestore implementation locally. It accepts the same gRPC protocol as production Firestore but requires no credentials and stores data only in memory.Installing and Starting the Emulator
Install the Google Cloud CLI
Download and install the gcloud CLI from cloud.google.com/sdk, then install the Firestore emulator component:
Set the environment variable
The Google client libraries detect the
FIRESTORE_EMULATOR_HOST environment variable and route all traffic to the emulator automatically:When you call
FirestoreDB(emulator_host=...) or db.use_emulator(...), the ODM sets os.environ["FIRESTORE_EMULATOR_HOST"] automatically. You do not need to export the variable separately in your test process — but if you also start the emulator in a subprocess, you may still need it there.Unit Testing with mock_firestore_for_tests()
For pure unit tests that should not touch any network or emulator, call mock_firestore_for_tests() on your FirestoreDB instance. This replaces the internal AsyncClient with a unittest.mock.MagicMock:
MagicMock client, every call to db.client.* returns a new mock. You can configure return values using the standard unittest.mock API to test your application logic without hitting Firestore.
pytest-asyncio Setup
All ODM methods areasync, so your tests need an async-capable test runner. Install pytest-asyncio and configure it in pytest.ini or pyproject.toml:
pyproject.toml:
asyncio_mode = auto, pytest-asyncio handles the event loop for every async def test_* function automatically — no @pytest.mark.asyncio decorator needed on individual tests.
conftest.py with Emulator Fixtures
Create a conftest.py at the root of your test directory to share the database fixture across all tests. The fixture below detects the FIRESTORE_EMULATOR_HOST environment variable and configures FirestoreDB accordingly:
Example Integration Test
With the fixtures above, writing integration tests looks identical to writing application code:Docker Compose for CI
For continuous integration pipelines, spin up the emulator as a service container. The repository ships with adocker-compose.test.yml that starts the official Google Cloud SDK emulator image:
Switching Back to Production
If your test suite usesuse_emulator() to toggle mid-session, call clear_emulator() to reconnect to the production Firestore endpoint and discard the emulator configuration:
clear_emulator() unsets FIRESTORE_EMULATOR_HOST from the environment and recreates the underlying AsyncClient pointed at the real GCP backend.
Summary
Emulator vs. MagicMock — when to use each
Emulator vs. MagicMock — when to use each
Use the emulator when:
- You want to test actual Firestore query semantics (filters, ordering, pagination, collection group queries).
- You are testing subcollection relationships or cascade delete logic.
- You want to catch issues with field aliases, serialization, or Pydantic validation against real data.
- You are testing business logic that calls model methods but you do not care about the Firestore responses.
- You want the fastest possible test execution with no external dependencies.
- You are writing tests for service or controller layers that sit above the ODM.
Database Client
Learn about FirestoreDB configuration options and credential handling.
Querying
Explore the full filter, ordering, and pagination query API.
