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

Firestore规则单元测试抛出Transaction lock timeout错误求助

Firestore规则单元测试并行运行时"Transaction lock timeout."问题解决方案

问题根源

多个测试文件并行运行时,每个文件/用例单独调用initializeTestEnvironment,共享同一个Firestore模拟器实例,引发资源竞争,导致事务锁超时。Jest默认并行执行测试文件,会加剧这种冲突。

解决方案

方案1:全局复用测试环境(推荐)

创建全局初始化脚本,只初始化一次测试环境,所有测试文件复用该实例,统一清理资源。

  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 };
  1. 修改测试文件,导入全局环境:
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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 07:07:52