研发协作
需要创建、隔离、匿名和清理测试数据时:用工厂…
适用于测试数据管理:为单元、集成、API、E2E 测试生成确定性、隔离、真实且合规的数据,并处理种子、匿名化、清理与并行执行。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 49/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 85/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
需要创建、隔离、匿名和清理测试数据时:用通用
解决思路
- 按场景路由:需要每测试可覆盖的新实体数据→工厂模式→`references/factories.md`;mock API 响应或 golden file→Fixture 策略→`references/factories.md`;把生产数据复制到非生产→数据匿名化;幂等填充测试 DB/参考数据→Database Seeding→`references/seeding-and-synthetic.md`;测试后清理/并行隔离→Cleanup Strategies→`references/seeding-and-synthetic.md`;生成边界值→Synthetic Data→`references/seeding-and-synthetic.md`。
- 先检查 `.agents/qa-project-context.md`;若存在则以其为基础,跳过其中已回答的问题。
- 盘点当前测试数据实践:如何创建、是否共享、如何清理、是否有种子脚本及其幂等性。
- 确认隐私合规:是否处理 PII、是否有 GDPR/HIPAA/PCI-DSS 要求、测试环境是否使用生产数据。
- 评估规模复杂度:数据量、关系复杂度、跨服务依赖。
- 让每个测试拥有自己的数据:测试只创建所需数据、基于该数据断言、并自行清理,以支持并行和消除顺序依赖。
- 动态实体数据用工厂,不用静态 fixture;静态 fixture 用于只读参考数据(国家代码、货币列表)。
- 使用工厂默认值 + 每次测试覆盖:`userFactory.build({ role: 'admin' })` 只覆盖测试关心的字段。
- 唯一字段用序列:Fishery `sequence`、FactoryBot `sequence(:email)`、Factory Boy `factory.Sequence`,不要硬编码 ID/email。
- 变体用 traits:`:admin`、`:inactive`、`out_of_stock`、`discounted`,避免为每个组合建 fixture 文件。
- 关联用 association:一个工厂构建另一个(order 构建 user),保持引用结构。
- 决策规则:数据有生命周期(测试中创建/修改/删除)用 factory;只读参考材料用 fixture 文件。
- 静态 fixture(JSON/YAML)用于 API 响应 mock(`page.route` + `route.fulfill`)、配置数据、golden file 比较。
- 动态 fixture(Playwright):用 `test.extend` 在测试前通过 API 创建数据,在 `await use(...)` 后删除;标准每测试 setup/teardown。
- Fixture 组合:在单个 `test.extend` 中组合工厂数据(`userFactory.build()`、`orderFactory.buildList(3)`),一步完成种子和清理。
- 生产数据用于测试前必须匿名化:替换 PII 为合成等价物,同时保留分布和关系。
- PII 掩码规则:Email 用 Faker email 保留原域名模式,例 `jane.doe@acme.com` -> `user-7291@test.example.com`;姓名 -> Faker name;电话 -> Faker phone 保留格式;地址 -> Faker address 保留国家/地区;SSN/National ID -> 测试模式 `000-00-0001`;信用卡 -> 测试卡号 `4242-4242-4242-4242`;出生日期 -> 固定偏移。
- 匿名化管道:使用 seeded Faker 保证确定性,使用内存 lookup table,按父记录优先顺序处理,并用包裹事务;匿名化用户 email 时同步更新所有引用(orders、comments、audit logs);先父后子,用同一 lookup,全部在一个事务内。
- GDPR 清单:任何非生产环境不得有真实 PII;匿名化不可逆(不存储映射回原值的 lookup table);保留数据分布;不能通过准标识符组合重识别;测试环境应用数据保留策略(N 天后自动删除);匿名化管道自动运行而非手动。
- 确保确定性:断言中避免 `Math.random()`、`Date.now()`、自增 ID;使用 seeded random generators(`faker.seed(n)`)、固定时间戳、工厂序列;若要断言 UUID,用 seeded `faker.string.uuid()` 保持跨运行稳定。
- 最小化数据:只创建测试需要的数据;用户搜索测试不需要完整用户资料、账单地址、支付方式、订单历史。
- 幂等种子脚本:使用 upsert `INSERT ... ON CONFLICT (natural_key) DO UPDATE SET ...`,以稳定自然键而非主键为键;不要用 `DELETE` 再 `INSERT` 的 reset,因为它破坏外键并重分配 serial ID。
- 如使用 DB 分支:Neon Branching 提供 copy-on-write Postgres 分支,秒级且带数据,适合临时预览环境;Supabase Branching 用 `supabase branches create pr-123` 克隆 schema,并可选择从备份克隆数据,预览环境指向分支 URL;PlanetScale Branching 默认 schema-only 无数据,仍需给分支 seed;注意 PlanetScale 2024 年 4 月移除免费 Hobby tier,MySQL 约 $39/mo 起,Postgres 约 $5/mo 起。
- 避免 Snaplet (hosted):2024-08-31 停止服务;`@snaplet/seed` 变为 `supabase-community/seed`(社区维护,最后有意义版本 v0.98.0,2024-07,之后无功能开发);新项目优先用上述 DB 分支 provider 加工厂生成种子。
- 选择数据策略:每测试 setup/teardown 用于会修改数据的测试,完全隔离、并行安全但慢;每套件 seed 用于只读参考数据,快但测试不能修改;每 worker seed 用于 Playwright 并行 worker,平衡速度与隔离,需要 worker-scoped fixtures;全局 seed 用于环境引导,只运行一次建立基线,必须幂等且有共享状态风险。
- 清理策略:事务回滚用于有直接 DB 访问的单元/集成测试,最快;截断 `TRUNCATE ... CASCADE` 用于套件间重置表,中等;API 清理用于无直接 DB 访问的 E2E,最慢。
- 注意事务回滚不能清理 E2E:应用自己打开 DB 连接,测试侧事务无法撤销应用写入;E2E 用 API 清理,按创建相反顺序删除。
- 合成数据生成:用可复用数组/helper 生成边界和异常值:`edgeCaseStrings`(空、空白、超长、XSS、SQL 注入、null/控制字符、RTL override)、`edgeCaseDates`、`boundaryValues(min, max)`,并通过 `test.each` 驱动。
- 反模式:避免共享可变测试数据;每个测试用工厂创建自己的数据。
- 反模式:避免未匿名化生产数据;必须匿名化或生成匹配生产分布的合成数据。
- 反模式:避免非确定性数据;使用 seeded Faker 和固定时间戳。
- 反模式:避免无清理策略;每次创建数据必须有对应清理。
- 反模式:避免 fixture 文件爆炸;用带 traits 的工厂,fixture 仅用于静态参考数据和 API 响应 mock。
- 反模式:避免过度指定测试数据;只覆盖测试关心的字段。
- 反模式:避免硬编码 ID;用工厂序列或 seeded UUID。
- 验证种子幂等:连续运行种子两次并 diff 行数,如 `psql -c "SELECT count(*) FROM countries" && <seed> && psql -c "SELECT count(*) FROM countries"` 两次返回相同数字且退出 0;增长说明缺少 `ON CONFLICT`。
- 验证无共享可变状态:在并行和随机顺序下运行套件:`npx playwright test --workers=4` 或 `pytest -n auto -p randomly` 保持通过;只在此失败说明顺序或共享数据依赖。
- 验证确定性:同一数据生成测试运行两次;设置 `faker.seed(n)` 后生成的名字/ID/UUID 跨运行一致;漂移说明有未 seed 的 Faker 调用或 `Date.now()`/`crypto.randomUUID()` 泄漏。
- 验证无真实 PII:`grep -rE '@(gmail|outlook|yahoo)\.com|[0-9]{3}-[0-9]{2}-[0-9]{4}' tests/ fixtures/` 无输出;有则存在匿名化缺口。
- 验证清理回到基线:套件前快照行数,运行,再快照;测试 DB 回到基线且无孤儿记录。
- 完成标准:套件创建的每个实体类型都有 factory 或 fixture,测试中无内联临时对象字面量;grep 测试目录中手工构建 fixture 并确认无残留。
- 完成标准:测试数据每测试隔离;套件在并行开启(`--workers=N` / `pytest -n auto`)且在随机顺序(`--shuffle` / `-p randomly`)下通过。
- 完成标准:种子脚本幂等;连续运行两次产生相同行数且退出 0;CI 作业无人工干预运行。
- 完成标准:测试 fixture 中无真实 PII;所有敏感数据已匿名化或合成;grep 生产域名/真实 SSN 无输出。
- 完成标准:数据清理已验证;套件后测试 DB 行数回到基线,无孤儿记录累积。
- 参考文件:`references/factories.md` 包含 Fishery(关联、确定性 UUID)、FactoryBot(User + Product traits)、Factory Boy(`Params`/`Trait`)、静态/动态/组合 Playwright fixtures、worker-scoped seeding fixture。
- 参考文件:`references/seeding-and-synthetic.md` 包含幂等 `ON CONFLICT` seed 脚本、Faker.js 匿名化 + 引用完整性管道、清理策略(rollback / truncate / API)、合成边界值生成器。
适用边界 · 注意事项
- 不适用于测试数据库自身的迁移/完整性测试;这类需求应使用 database-testing。
- 不适用于环境供应和数据库分支策略本身;这类需求应使用 test-environments。
- 不适用于不需要创建/管理测试数据的纯静态逻辑测试;可直接使用相应单元测试技能。