Firestore规则单元测试抛出Transaction lock timeout错误求助
Firestore规则单元测试并行运行时"Transaction lock timeout."问题解决方案
问题根源
多个测试文件并行运行时,每个文件/用例单独调用initializeTestEnvironment,共享同一个Firestore模拟器实例,引发资源竞争,导致事务锁超时。Jest默认并行执行测试文件,会加剧这种冲突。
解决方案
方案1:全局复用测试环境(推荐)
创建全局初始化脚本,只初始化一次测试环境,所有测试文件复用该实例,统一清理资源。
- 创建
setupTests.ts全局配置文件:
import { initializeTestEnvironment, RulesTestEnvironment } from "@firebase/rules-unit-testing"; import * as fs from 'fs'; const MY_PROJECT_ID = "projectId"; let testEnv: RulesTestEnvironment; // 所有测试前初始化一次环境 beforeAll(async () => { testEnv = await initializeTestEnvironment({ projectId: MY_PROJECT_ID, firestore: { rules: fs.readFileSync("firestore.rules", "utf8"), host: '127.0.0.1', port: 8080 }, }); }); // 所有测试完成后统一清理 afterAll(async () => { await testEnv.cleanup(); }); export { testEnv };
- 修改测试文件,导入全局环境:
import { assertSucceeds } from "@firebase/rules-unit-testing" import { setDoc } from "firebase/firestore"; import { describe, test, beforeEach } from "@jest/globals"; import { testEnv } from "../setupTests"; describe("app", () => { // 每个测试前清空数据,避免测试间污染 beforeEach(async () => { await testEnv.clearFirestore(); }); test("test1", async () => { const user = testEnv.authenticatedContext("PE8T3x91fsOw9YZj4fkiXEdjZNsE", { firebase: { sign_in_provider: "google.com", } }); const reference = user.firestore().collection("profile").doc("user"); await assertSucceeds(setDoc(reference, {})); }); })
方案2:串行运行测试
关闭Jest并行执行,让测试文件逐个运行,避免资源竞争。
修改package.json中的测试命令:
{ "scripts": { "test": "jest --runInBand" } }
或在jest.config.js中配置:
module.exports = { // 其他配置... runInBand: true };
方案3:为每个测试文件使用独立Project ID
每个测试文件生成唯一的Project ID,模拟器会为不同ID维护独立数据空间,避免冲突。
修改测试文件:
import { assertSucceeds, initializeTestEnvironment } from "@firebase/rules-unit-testing" import { setDoc } from "firebase/firestore"; import { describe, test, beforeAll, afterAll } from "@jest/globals"; import * as fs from 'fs'; // 生成唯一Project ID const MY_PROJECT_ID = `projectId-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`; describe("app", () => { let testEnv; beforeAll(async () => { testEnv = await initializeTestEnvironment({ projectId: MY_PROJECT_ID, firestore: { rules: fs.readFileSync("firestore.rules", "utf8"), host: '127.0.0.1', port: 8080 }, }); }); afterAll(async () => { await testEnv.cleanup(); }); test("test1", async () => { const user = testEnv.authenticatedContext("PE8T3x91fsOw9YZj4fkiXEdjZNsE", { firebase: { sign_in_provider: "google.com", } }); const reference = user.firestore().collection("profile").doc("user"); await assertSucceeds(setDoc(reference, {})); }); })
方案对比
- 方案1:性能最优,资源占用少,无冲突,是推荐方案。
- 方案2:实现简单,但测试速度会变慢,适合小型测试集。
- 方案3:避免冲突,但每个测试都初始化环境,速度较慢,模拟器资源占用高。
额外注意事项
- 确保每个测试前清空Firestore数据,避免测试间数据污染。
- 若仍有资源泄漏提示,可运行
jest --detectOpenHandles排查未关闭的连接或定时器。
内容的提问来源于stack exchange,提问作者flutroid
相关产品推荐
相关产品推荐

