You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何对Express封装的Firebase云函数进行单元测试?

测试基于Express的Firebase HTTPS云函数

我之前刚好踩过这个坑,给你分享一套完整的单元测试方案,亲测好用!核心思路是直接测试Express应用实例,不用依赖Firebase的部署环境,再结合Jest和Supertest模拟HTTP请求,涉及Firebase服务时用Mock隔离真实依赖。

第一步:准备测试工具

先安装TypeScript环境下的必要依赖:

npm install --save-dev jest supertest @types/jest @types/supertest ts-jest
  • jest:Firebase官方推荐的测试框架
  • supertest:专门用于测试HTTP服务器(比如Express)的工具,能模拟各类HTTP请求
  • 带@types/的包是TypeScript的类型定义文件

然后在项目根目录创建jest.config.js,配置TypeScript支持:

module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'node',
  roots: ['<rootDir>/src'],
  testMatch: ['**/*.test.ts'],
};

第二步:调整云函数代码

关键是单独导出Express的app实例,方便测试时直接导入使用,而不是只导出Firebase的onRequest handler。比如你的src/index.ts可以改成这样:

import * as functions from 'firebase-functions';
import * as express from 'express';
const cors = require('cors')({ origin: true });

// 单独导出app实例,供测试使用
export const app = express();

// 应用中间件和路由
app.use(cors);
app.use(express.json()); // 处理JSON请求体

// 示例GET路由
app.get('/hello', (req, res) => {
  res.status(200).json({ message: 'Hello from Firebase Express!' });
});

// 示例POST路由
app.post('/submit', (req, res) => {
  const { content } = req.body;
  if (!content) {
    return res.status(400).json({ error: 'Content is required' });
  }
  res.status(201).json({ success: true, received: content });
});

// 导出Firebase云函数handler
export const api = functions.https.onRequest(app);

第三步:编写基础路由测试用例

创建测试文件src/index.test.ts,用Supertest模拟请求,Jest做断言:

import request from 'supertest';
import { app } from './index';

describe('Express-based Firebase Cloud Function Tests', () => {
  // 测试GET /hello路由
  test('GET /hello should return 200 with correct message', async () => {
    const response = await request(app).get('/hello');
    expect(response.statusCode).toBe(200);
    expect(response.body.message).toBe('Hello from Firebase Express!');
  });

  // 测试POST /submit:参数缺失的情况
  test('POST /submit should return 400 when content is missing', async () => {
    const response = await request(app).post('/submit').send({});
    expect(response.statusCode).toBe(400);
    expect(response.body.error).toBe('Content is required');
  });

  // 测试POST /submit:正常提交的情况
  test('POST /submit should return 201 with received content', async () => {
    const testContent = 'Test message';
    const response = await request(app)
      .post('/submit')
      .send({ content: testContent });
    expect(response.statusCode).toBe(201);
    expect(response.body.success).toBe(true);
    expect(response.body.received).toBe(testContent);
  });
});

第四步:测试依赖Firebase服务的路由

如果路由用到Firestore、Auth等服务,不要连接真实项目,用Jest的Mock功能模拟这些服务。比如测试一个查询Firestore的路由:

先修改路由代码(新增查询用户接口):

import * as admin from 'firebase-admin';
admin.initializeApp();
const db = admin.firestore();

app.get('/user/:userId', async (req, res) => {
  try {
    const userDoc = await db.collection('users').doc(req.params.userId).get();
    if (!userDoc.exists) {
      return res.status(404).json({ error: 'User not found' });
    }
    res.status(200).json(userDoc.data());
  } catch (err) {
    res.status(500).json({ error: 'Internal server error' });
  }
});

对应的测试用例(Mock Firestore):

import * as admin from 'firebase-admin';
import request from 'supertest';
import { app } from './index';
import * as functionsTest from 'firebase-functions-test';

// 初始化Firebase测试工具(离线模式,不连接真实项目)
const test = functionsTest();

// Mock Firebase Admin的Firestore模块
jest.mock('firebase-admin', () => {
  const mockFirestore = {
    collection: jest.fn(() => ({
      doc: jest.fn(() => ({
        get: jest.fn(() => Promise.resolve({
          exists: true,
          data: () => ({ userId: '123', username: 'MockUser' })
        }))
      }))
    }))
  };
  return {
    initializeApp: jest.fn(),
    firestore: jest.fn(() => mockFirestore)
  };
});

describe('Firestore-dependent Route Tests', () => {
  // 测试结束后清理测试环境
  afterAll(() => {
    test.cleanup();
  });

  test('GET /user/123 should return user data', async () => {
    const response = await request(app).get('/user/123');
    expect(response.statusCode).toBe(200);
    expect(response.body.username).toBe('MockUser');
  });

  test('GET /user/456 should return 404 when user not found', async () => {
    // 临时修改Mock返回,模拟用户不存在的情况
    (admin.firestore() as jest.Mock).mockReturnValue({
      collection: jest.fn(() => ({
        doc: jest.fn(() => ({
          get: jest.fn(() => Promise.resolve({ exists: false }))
        }))
      }))
    });
    const response = await request(app).get('/user/456');
    expect(response.statusCode).toBe(404);
    expect(response.body.error).toBe('User not found');
  });
});

运行测试

在package.json的scripts里添加测试命令:

"scripts": {
  "test": "jest"
}

然后执行:

npm test

这样就能在本地完整测试你的Express云函数,不管是简单路由逻辑还是依赖Firebase服务的复杂场景都能覆盖到~

内容的提问来源于stack exchange,提问作者DauleDK

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 10:40:37