firebase-testing

janisto's avatarfrom janisto

Guide for testing Firebase Admin SDK with Vitest mocks. Use when writing tests that involve Firebase Auth, Firestore, or Firebase App.

⭐0stars🔀0forks📁View on GitHub🕐Updated Dec 28, 2025

When & Why to Use This Skill

This Claude skill provides comprehensive patterns and templates for testing the Firebase Admin SDK using Vitest mocks. It enables developers to efficiently mock Firebase Auth, Firestore, and App instances, facilitating robust unit testing and integration testing for backend services without the need for live Firebase environments or real credentials.

Use Cases

  • Testing authentication middleware and protected routes by mocking valid, invalid, and revoked Firebase ID tokens.
  • Simulating complex Firestore database operations, including collection queries and document CRUD actions, to verify business logic in isolation.
  • Setting up automated test suites in CI/CD pipelines that require mocked Firebase services to ensure fast, reliable, and cost-effective execution.
  • Implementing advanced Vitest mocking strategies such as module-level mocking, dynamic imports, and module cache resetting for Firebase-dependent plugins.
namefirebase-testing
descriptionGuide for testing Firebase Admin SDK with Vitest mocks. Use when writing tests that involve Firebase Auth, Firestore, or Firebase App.

Firebase Testing Patterns

This skill provides patterns for mocking Firebase Admin SDK in Vitest tests.

Mock Helpers Location

Firebase mocks are located in app/tests/mocks/firebase.ts and provide:

  • createFirebaseAppMock() - Mock Firebase App instance
  • createFirebaseAuthMock() - Mock Firebase Auth with verifyIdToken, getUser
  • createFirestoreMock() - Mock Firestore with collection, doc, get
  • resetFirebaseMocks() - Reset all mocks between tests

Firebase Plugin Test Template

import Fastify from "fastify";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import {
  createFirebaseAppMock,
  createFirebaseAuthMock,
  createFirestoreMock,
} from "../../mocks/firebase.js";

const mockApp = createFirebaseAppMock();
const mockAuth = createFirebaseAuthMock();
const mockFirestore = createFirestoreMock();

vi.mock("firebase-admin/app", () => ({
  getApps: vi.fn(() => [mockApp]),
  initializeApp: vi.fn(() => mockApp),
  cert: vi.fn(),
}));

vi.mock("firebase-admin/auth", () => ({
  getAuth: vi.fn(() => mockAuth),
}));

vi.mock("firebase-admin/firestore", () => ({
  getFirestore: vi.fn(() => mockFirestore),
}));

describe("Firebase Plugin", () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  afterEach(async () => {
    // Clear module cache so dynamic imports get fresh mocked modules
    vi.resetModules();
  });

  it("should register firebase decorator", async () => {
    const { default: firebasePlugin } = await import(
      "../../../src/plugins/firebase.js"
    );
    const fastify = Fastify();
    await fastify.register(firebasePlugin);
    await fastify.ready();

    expect(fastify.firebase).toBeDefined();
    await fastify.close();
  });
});

Auth Testing Patterns

Valid Token Authentication

it("should authenticate with valid token", async () => {
  const mockDecodedToken = {
    uid: "test-user-123",
    email: "test@example.com",
    email_verified: true,
  };
  mockAuth.verifyIdToken.mockResolvedValue(mockDecodedToken);

  const response = await fastify.inject({
    method: "GET",
    url: "/protected",
    headers: { authorization: "Bearer valid-token" },
  });

  expect(response.statusCode).toBe(200);
  expect(mockAuth.verifyIdToken).toHaveBeenCalledWith("valid-token", false);
});

Invalid Token Handling

it("should return 401 for invalid token", async () => {
  mockAuth.verifyIdToken.mockRejectedValue(
    new Error("Firebase ID token has invalid signature"),
  );

  const response = await fastify.inject({
    method: "GET",
    url: "/protected",
    headers: { authorization: "Bearer invalid-token" },
  });

  expect(response.statusCode).toBe(401);
});

Token Revocation

it("should return 401 when token is revoked", async () => {
  const revokedError = Object.assign(new Error("Token has been revoked"), {
    code: "auth/id-token-revoked",
  });
  mockAuth.verifyIdToken.mockRejectedValue(revokedError);

  const response = await fastify.inject({
    method: "GET",
    url: "/protected",
    headers: { authorization: "Bearer revoked-token" },
  });

  expect(response.statusCode).toBe(401);
  expect(response.json().message).toContain("Token has been revoked");
});

Missing Authorization Header

it("should return 401 when no authorization header", async () => {
  const response = await fastify.inject({
    method: "GET",
    url: "/protected",
  });

  expect(response.statusCode).toBe(401);
});

Firestore Testing Patterns

Mocking Collection Queries

it("should query Firestore collection", async () => {
  const mockDocs = [
    { id: "doc1", data: () => ({ name: "Test 1" }) },
    { id: "doc2", data: () => ({ name: "Test 2" }) },
  ];
  mockFirestore.collection.mockReturnValue({
    get: vi.fn().mockResolvedValue({ docs: mockDocs }),
    limit: vi.fn().mockReturnThis(),
  });

  // Test code that queries Firestore
});

Mocking Document Operations

it("should get a document", async () => {
  const mockDoc = {
    exists: true,
    id: "doc-id",
    data: () => ({ field: "value" }),
  };
  mockFirestore.collection.mockReturnValue({
    doc: vi.fn().mockReturnValue({
      get: vi.fn().mockResolvedValue(mockDoc),
    }),
  });

  // Test code that gets a document
});

Key Patterns

  1. Mock before import: Use vi.mock() at module level before importing tested modules
  2. Dynamic imports: Use await import() after mocks are set up to ensure mocks are applied
  3. Reset between tests:
    • vi.clearAllMocks() in beforeEach - clears mock call history
    • vi.resetModules() in afterEach - clears module cache so dynamic imports get fresh mocked modules
  4. Realistic data: Use realistic mock data that matches Firebase structures

Commands

cd app
npm run test              # Run all tests
npm run test:coverage     # Run tests with coverage

Boundaries

  • Never use real Firebase credentials in tests
  • Always mock Firebase Admin SDK modules
  • Use the shared mock helpers from tests/mocks/firebase.ts