I noticed there are many articles about creating unit tests with Firestore Emulators, but I find them most useful for building apps. You can run the full Firebase Local Suite without touching your live data in the cloud or creating an extra development database.
TL;DR#
Always remember to install Java JDK. Follow the firebase init
instructions. Connect the emulators to your app in development mode only, and use the authentication to generate a random fake user for you.
Emulator Setup#
- Install Java JDK
The Firebase Emulator Suite is built upon the Java JDK. It must be installed first.
- Make sure you have Firebase Tools installed globally.
npm install -g firebase-tools
- Initialize Firebase inside your project.
firebase init
- Select
Emulators
and whatever features you want to test locally.
- Set up the
default
project.
The full set of Firebase Emulators requires you to link with an existing project. However, you do not have to modify your cloud version if you’re spinning up a project for local testing.
- Use the defaults for
firestore.rules
andfirestore.indexes.json
files. InstallTypeScript
because you’re a professional with standards who writes quality code. Useeslint
for testing, and go ahead and install dependencies. - Finally, select the default ports and add the emulators you want to use, like
Authentication
,Functions
, andFirestore
.
🗈 If you’re using any of my test projects, use firebase use
to connect your local instance to an active database in your Firebase Cloud.
Emulator Helper#
I like to add a helper in package.json
so that the emulators can be run without switching directories. This is particularly useful with Firebase Functions Emulators.
"emulators": "npm run build --prefix functions & firebase emulators:start"
Functions with Eslint#
Edit functions/.eslintrc.js
to include .eslintrc.js
in the ignore file.
ignorePatterns: [
"/lib/**/*", // Ignore built files.
".eslintrc.js"
],
Also, change your rules to fit your style of writing code.
module.exports = {
root: true,
env: {
es6: true,
node: true,
},
extends: [
"plugin:import/errors",
"plugin:import/warnings",
"plugin:import/typescript",
"google",
"plugin:@typescript-eslint/recommended",
],
parser: "@typescript-eslint/parser",
parserOptions: {
project: ["tsconfig.json", "tsconfig.dev.json"],
sourceType: "module",
tsconfigRootDir: __dirname,
},
ignorePatterns: [
"/lib/**/*", // Ignore built files.
".eslintrc.js"
],
plugins: [
"@typescript-eslint",
"import",
],
rules: {
"quotes": ["error", "single"],
'object-curly-spacing': ['error', 'always'],
"import/no-unresolved": 0,
"indent": ["error", 4],
},
};
Firebase Setup#
Create a lib/firebase.ts
file and add emulator support. Your framework implementation may vary slightly.
// emulators
if (dev) {
connectFirestoreEmulator(db, 'localhost', 8080);
connectAuthEmulator(auth, 'http://localhost:9099');
connectFunctionsEmulator(functions, "http://localhost", 5001);
}
Here is a full example file.
import { PUBLIC_FIREBASE_CONFIG } from '$env/static/public';
import { dev } from '$app/environment';
import { getApps, initializeApp } from 'firebase/app';
import { connectAuthEmulator, getAuth } from 'firebase/auth';
import { connectFirestoreEmulator, getFirestore } from 'firebase/firestore';
import { connectFunctionsEmulator, getFunctions } from 'firebase/functions';
const firebase_config = JSON.parse(
PUBLIC_FIREBASE_CONFIG
);
// initialize and login
if (!getApps().length) {
initializeApp(firebase_config);
}
export const auth = getAuth();
export const db = getFirestore();
export const functions = getFunctions();
// emulators
if (dev) {
connectFirestoreEmulator(db, 'localhost', 8080);
connectAuthEmulator(auth, 'http://localhost:9099');
connectFunctionsEmulator(functions, "http://localhost", 5001);
}
📝 The ports are imported differently depending on the service.
Angular#
If you’re using Angular, you must import it into each of the providers array.
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
provideClientHydration(),
// Angular Providers
importProvidersFrom(
provideFirebaseApp(() => initializeApp(firebaseConfig)),
provideFirestore(() => {
const firestore = getFirestore();
connectFirestoreEmulator(firestore, 'localhost', 8080);
return firestore;
}),
provideAuth(() => getAuth()),
// provideStorage(() => getStorage()),
// provideAnalytics(() => getAnalytics()),
)
]
};
Running the Emulators#
Thanks to our emulators helper, we can run the emulators in our root directory with npm run emulators
. We should get a second window that opens. Minimize that. Our main terminal will have the emulator links. We can click ctrl+
and the link to open a new window with our emulators.
Authentication#
When we want to login to our app, we must create a fake profile.
Clicking on add a new account
allows us to emulate a real user.
We can then sign in with this fake user to test our app.
Firestore Emulator#
The Firestore Emulator will be in purple to clarify that it is a development environment.
Everything else should work as expected. You can now test your app on your local machine without modifying your live data.
Seeding Data#
To seed your data, you must write your own functions. You must connect to Firebase Admin, run the function only in dev mode, and call it in your package.json
file with a command or in your app only in development mode.
⚠️ All data is lost when you stop the emulators.
Test Projects#
You can view some of the projects in my Counters posts for Firebase Emulator examples.
Now go test locally!
J