Firestore规则测试持续超时的问题排查求助
测试Firestore安全规则时的超时问题排查与解决
问题背景
测试自定义Firestore安全规则时,运行测试用例会触发超时错误;延长Jest超时时间后测试能通过,但仍会出现Firestore客户端无法连接后端的错误日志,不符合预期。
环境与配置
1. Firestore模拟器启动
通过以下命令启动模拟器,启动状态正常,Firestore模拟器地址为127.0.0.1:8080,可通过浏览器访问:
❯ npm run firebase:run:emulator > warzywniax@0.0.0 firebase:run:emulator > cd firebase && firebase emulators:start --project=warzywniax --import=./seed --export-on-exit i emulators: Starting emulators: auth, firestore, storage i firestore: Importing data from /firebase/seed/firestore_export/firestore_export.overall_export_metadata i firestore: Firestore Emulator logging to firestore-debug.log ✔ firestore: Firestore Emulator UI websocket is running on 9150. i auth: Importing config from /firebase/seed/auth_export/config.json i auth: Importing accounts from /firebase/seed/auth_export/accounts.json i ui: Emulator UI logging to ui-debug.log ┌─────────────────────────────────────────────────────────────┐ │ ✔ All emulators ready! It is now safe to connect your app. │ │ i View Emulator UI at http://127.0.0.1:4000/ │ └─────────────────────────────────────────────────────────────┘ ┌────────────────┬────────────────┬─────────────────────────────────┐ │ Emulator │ Host:Port │ View in Emulator UI │ ├────────────────┼────────────────┼─────────────────────────────────┤ │ Authentication │ 127.0.0.1:9099 │ http://127.0.0.1:4000/auth │ ├────────────────┼────────────────┼─────────────────────────────────┤ │ Firestore │ 127.0.0.1:8080 │ http://127.0.0.1:4000/firestore │ ├────────────────┼────────────────┼─────────────────────────────────┤ │ Storage │ 127.0.0.1:9199 │ http://127.0.0.1:4000/storage │ └────────────────┴────────────────┴─────────────────────────────────┘ Emulator Hub running at 127.0.0.1:4400 Other reserved ports: 4500, 9150
2. Jest初始化脚本
在setupFilesAfterEnv钩子中初始化测试环境:
import { initializeTestEnvironment, RulesTestEnvironment } from '@firebase/rules-unit-testing'; import firebase from 'firebase/compat/'; export let testEnvironment: RulesTestEnvironment; export let guestUserContext: firebase.firestore.Firestore; beforeAll(async () => { testEnvironment = await initializeTestEnvironment({ firestore: { host: '127.0.0.1', port: 8080, }, projectId: 'warzywniax', }); guestUserContext = testEnvironment.unauthenticatedContext().firestore(); console.info('TEST_ENV is ready:\n', testEnvironment); console.info('GUEST_USER is ready:\n', guestUserContext); });
3. 测试用例
运行的简单测试用例:
import { assertSucceeds } from '@firebase/rules-unit-testing'; import { guestUserContext } from '../integration-setup'; describe('Offers collection tests', () => { it('should allow to read offers for any user', async () => { const testQuery = guestUserContext.collection('offers'); expect(await assertSucceeds(testQuery.get())); }); });
错误现象
- 初始运行测试时触发Jest超时:
FAIL test/integration/firebase/firestore.rules.spec.ts (5.234 s) Offers collection tests ✕ should allow to read offers for any user (5001 ms) ● Offers collection tests › should allow to read offers for any user thrown: "Exceeded timeout of 5000 ms for a test. Add a timeout value to this test to increase the timeout, if this is a long-running test. See https://jestjs.io/docs/api#testname-fn-timeout.
- 延长超时至30秒(
--testTimeout=30000)后,测试通过但出现Firestore连接错误日志:
console.error [2023-03-30T15:45:44.552Z] @firebase/firestore: Firestore (9.17.1): Could not reach Cloud Firestore backend. Backend didn't respond within 10 seconds. This typically indicates that your device does not have a healthy Internet connection at the moment. The client will operate in offline mode until it is able to successfully connect to the backend. at Logger.defaultLogHandler [as _logHandler] (node_modules/@firebase/logger/src/logger.ts:115:57) at Logger.Object.<anonymous>.Logger.error (node_modules/@firebase/logger/src/logger.ts:210:5) at x (node_modules/@firebase/firestore/dist/index.esm2017.js:124:11) at Pu.au (node_modules/@firebase/firestore/dist/index.esm2017.js:13778:20) at nc.op (node_modules/@firebase/firestore/dist/index.esm2017.js:13748:14) at node_modules/@firebase/firestore/dist/index.esm2017.js:14307:14 at node_modules/@firebase/firestore/dist/index.esm2017.js:17414:73 at node_modules/@firebase/firestore/dist/index.esm2017.js:17447:54 PASS test/integration/firebase/firestore.rules.spec.ts (10.335 s) Offers collection tests ✓ should allow to read offers for any user (10168 ms)
可能原因与解决方案
1. 客户端默认尝试SSL连接模拟器
Firestore客户端默认启用SSL连接,但本地模拟器仅提供HTTP服务,导致连接协商延迟甚至失败。
解决方案:在初始化TestEnvironment时,显式添加ssl: false配置:
testEnvironment = await initializeTestEnvironment({ firestore: { host: '127.0.0.1', port: 8080, ssl: false, // 禁用SSL,适配模拟器的HTTP服务 }, projectId: 'warzywniax', });
2. WebSocket连接适配问题
Firestore客户端默认优先使用WebSocket连接,部分环境下WebSocket与模拟器的兼容性不佳,导致连接超时。
解决方案:添加experimentalForceLongPolling: true配置,强制使用长轮询:
testEnvironment = await initializeTestEnvironment({ firestore: { host: '127.0.0.1', port: 8080, ssl: false, experimentalForceLongPolling: true, // 强制长轮询 }, projectId: 'warzywniax', });
3. 测试环境未正确清理
测试结束后未关闭Firestore连接,残留的连接状态或离线缓存影响下一次测试,导致连接延迟。
解决方案:添加afterAll钩子清理测试环境:
afterAll(async () => { await testEnvironment.cleanup(); });
修改后的完整初始化脚本
import { initializeTestEnvironment, RulesTestEnvironment } from '@firebase/rules-unit-testing'; import firebase from 'firebase/compat/'; export let testEnvironment: RulesTestEnvironment; export let guestUserContext: firebase.firestore.Firestore; beforeAll(async () => { testEnvironment = await initializeTestEnvironment({ firestore: { host: '127.0.0.1', port: 8080, ssl: false, experimentalForceLongPolling: true, }, projectId: 'warzywniax', }); guestUserContext = testEnvironment.unauthenticatedContext().firestore(); }); afterAll(async () => { await testEnvironment.cleanup(); });
内容的提问来源于stack exchange,提问作者Krzysztof Kaczyński
相关产品推荐
相关产品推荐

