如何对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
相关产品推荐
相关产品推荐

