关于 database-testing
database-testing 是一项面向工程师的数据库测试技能,提供迁移、数据完整性、种子数据和查询性能的测试方法。它强调使用真实迁移路径、验证回滚,并让性能断言能够发现索引缺失。
主要功能
- 01正向与回滚迁移测试
- 02数据库约束与引用完整性验证
- 03确定性种子数据和测试隔离
- 04迁移漂移检测
- 05EXPLAIN 查询计划与索引断言
适用场景
- 01检查迁移是否保留现有数据
- 02验证约束能拒绝无效记录
- 03发现数据库结构与迁移不一致
- 04确认关键查询使用索引
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ petrkindlmann/ qa-skills --skill database-testing安装后在 Claude Code 中输入/database-testing确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/integrity-and-seed.mdmigration-tests.mdperformance-and-docker.md详细介绍
**开始之前:**检查 .agents/qa-project-context.md,了解数据库类型、ORM、迁移工具和环境配置——它们会影响以下每种模式。
探索问题
先检查 .agents/qa-project-context.md——如果文件存在,就使用其中的信息,并跳过已回答的问题。然后:
- **数据库类型:**PostgreSQL、MySQL、SQLite、MongoDB,还是多数据库?不同数据库的约束语法、迁移工具和性能分析方式各不相同。
- **ORM/查询构建器:**Prisma、TypeORM、Drizzle、Sequelize、SQLAlchemy、Django ORM,还是原始 SQL?ORM 决定了迁移工具和测试模式。
- **迁移工具:**Prisma Migrate、TypeORM migrations、Flyway、Liquibase、Alembic、knex,还是自定义工具?这决定了如何测试正向和反向迁移。
- **测试数据库策略:**每个测试使用独立数据库、事务回滚、Testcontainers,还是共享数据库并清理?这会影响速度和可靠性。
- **现有种子数据:**是否有工厂、固件或种子脚本?检查
prisma/seed.ts、seeds/、fixtures/或工厂模式。 - **性能基线:**是否已有查询基准测试或慢查询监控?
核心原则
-
**正向和反向迁移都要测试。**每个迁移都应可逆。如果回滚失败,就无法从错误部署中恢复。测试
down路径,而不只是up——并使用实际的还原机制测试(Prisma 使用手写的down.sql,其他工具使用原生还原命令),而不是元数据标记。 -
约束是第一道防线。
NOT NULL、UNIQUE、FOREIGN KEY和CHECK约束可以在数据库层阻止错误数据,不受应用代码影响。测试每项约束是否存在,以及是否会以正确的错误拒绝无效数据。 -
**使用确定性种子数据。**每次运行,测试都必须产生相同结果。使用固定 ID 和固定时间戳的工厂,而不是随机数据。在种子数据中使用未设置种子的
faker.random()、uuid()和now()会导致测试不确定。 -
**隔离每个测试的数据库状态。**共享状态的测试会受执行顺序影响,也容易不稳定。使用事务回滚、每个测试独立的数据库,或确保执行清理。
-
测试迁移,而不是 ORM 的同步功能。
prisma db push/typeorm synchronize: true会跳过用户实际运行的迁移路径。务必执行真正的迁移文件。 -
**无法失败的性能断言毫无价值。**在信任 EXPLAIN 测试通过之前,先确认删除索引时测试会失败。参见“验证”。
迁移测试
可运行的迁移测试代码请参见 references/migration-tests.md。
正向迁移验证
启动一个全新、空的数据库,使用 prisma migrate deploy 运行所有迁移,然后查询 information_schema,断言预期的表和列均已存在,且类型、可空性和默认值正确。优先使用 Testcontainers 提供的 DATABASE_URL;必须针对现有 Postgres 时,再使用管理员 Pool 执行 CREATE DATABASE 作为备用方案。每个测试套件选择一种策略,不要混用。
回滚测试
Prisma 没有 migrate down / migrate rollback 命令。prisma migrate resolve --rolled-back 不是回滚工具——它只用于修复 migrate deploy 失败的迁移;如果迁移已成功应用,则会抛出错误。对于可逆变更,受支持的测试方式是:正向应用迁移、捕获状态、直接运行手写的 down.sql(psql -f down.sql)、断言被还原的对象已不存在,然后重新应用迁移。每个迁移目录都应维护一个 down.sql。
对于 TypeORM 和 Sequelize,两者都自带原生回滚命令(dataSource.undoLastMigration()、sequelize-cli db:migrate:undo);用它们替换 psql -f down.sql 这一步即可——捕获 → 回滚 → 断言 → 重新应用的流程完全相同。
对于 Drizzle Kit v1.0(截至 2026 年年中仍处于 beta 阶段——最新版本为 [email protected],尚无稳定的正式版;如果需要稳定版本,保守选择 0.44.x):使用 drizzle-kit generate + drizzle-kit migrate。在 CI 中固定确切版本——v1 beta 系列重做了 casing API,并移除了 Postgres 的 RQB v1 ._query,且 API 在不同 beta 版本间仍在变化。Drizzle 不会生成向下迁移;与 Prisma 一样,请将自己的逆向 SQL 纳入版本控制。
迁移期间的数据保留
要测试新增列是否保留现有行,先应用迁移至 N-1,插入数据,然后应用待测迁移并断言行仍然存在(新的可空列会带有默认值或 null)。prisma migrate deploy 没有 --to 标志——它会应用所有待处理的迁移。要停在 N-1,请部署一个只包含截至 N-1 的迁移目录(在 CI 中暂存),然后再部署完整目录。对于支持真正定位迁移的工具(Flyway -target=、Alembic upgrade <rev>),则使用其原生标志。
迁移漂移检测
最常见的实际迁移错误是:有人修改了数据库或架构,却没有创建对应的迁移,因此已提交的迁移不再能还原 schema.prisma。prisma migrate diff --from-migrations … --to-schema-datamodel … --exit-code 在检测到漂移时会返回非零状态码——将其接入 CI,作为耗时较长的测试之前快速运行的预检步骤。参见 references/migration-tests.md。
架构快照对比
在迁移前后分别捕获 pg_dump --schema-only 快照,并逐表对比,确保只有预期的表发生变化。参见 references/migration-tests.md。
其他 ORM
TypeORM: 在测试中使用 migrationsRun: false 的 DataSource,然后调用 dataSource.runMigrations() 和 dataSource.undoLastMigration()。流程相同:应用所有迁移、验证架构、回滚最近一次迁移、验证回滚结果。
Alembic(Python): 在空数据库上测试 alembic upgrade head,使用 alembic downgrade base 执行完整回滚,并运行升级 → 降级 → 再升级的循环,以验证架构一致性。通过 fixture 使用全新的测试数据库。
数据完整性测试
可运行的约束和引用完整性代码,请参见 references/integrity-and-seed.md。
约束测试
断言每项约束都会拒绝无效数据:NOT NULL 拒绝缺少必填列的情况,UNIQUE 拒绝重复值,FOREIGN KEY 拒绝无效引用,CHECK 拒绝超出范围的值,ON DELETE CASCADE 删除依赖行。在 pool.query 层断言数据库错误消息(/null value in column/、/unique constraint/i 等),而不是在 ORM 或应用验证层断言,因为这些层可能会掩盖数据库约束缺失的问题。
引用完整性与数据质量审计
运行反连接查询(LEFT JOIN … WHERE parent.id IS NULL),断言没有记录孤儿指向已删除的父记录。然后审计预期完整性与实际强制执行的完整性之间的差距:COUNT(*) 与 COUNT(DISTINCT col) 的差异可标示出本应唯一却缺少约束的列;COUNT(*) FILTER (WHERE col IS NULL) 可标示出本应为非空的列。参见 references/integrity-and-seed.md。
数据类型验证
测试:以正确精度存储货币值(无浮点精度损失)、强制执行 VARCHAR 长度限制(溢出时显示 value too long),并将带时区的时间戳存储为 UTC——使用偏移量(+02:00)插入,读取后验证输出为 ISO UTC 格式。
种子数据管理
可运行的工厂、种子脚本和隔离代码,请参见 references/integrity-and-seed.md。
工厂模式(TypeScript)
使用 buildUser(overrides) 工厂构建记录,通过递增计数器生成稳定、确定性的 ID 和电子邮件,并使用固定时间戳(new Date('2026-01-01T00:00:00Z'),绝不使用不带参数的 new Date());同时使用 createUser(pool, overrides) 辅助函数插入并返回记录。请参见 references/integrity-and-seed.md。
Prisma 种子脚本
使用固定 ID 执行 upsert,确保种子操作具备幂等性且可重复运行;并根据 process.env.SEED_ENV 切换配置:test(最少数据,2–3 个用户)、staging(逼真的数据量,50+ 个用户)、demo(精选数据)。staging 和 demo 都扩展自 test。请参见 references/integrity-and-seed.md。
使用事务回滚进行测试隔离
将每个测试包装在 BEGIN/ROLLBACK 中,确保插入的数据不会在测试之间保留。模块级共享客户端适用于串行运行(jest --runInBand);同一 worker 中并行运行的测试文件需要使用每个测试套件各自的客户端或保存点。请参见 references/integrity-and-seed.md。
查询性能测试
可运行的 EXPLAIN ANALYZE 和索引验证代码,请参见 references/performance-and-docker.md。
EXPLAIN ANALYZE 模式
对关键查询运行 EXPLAIN (ANALYZE, FORMAT JSON),读取 plan.Plan['Node Type'],并断言其匹配 /Index/(而不是 Seq Scan),且 plan['Execution Time'] 低于阈值。请参见 references/performance-and-docker.md。
索引验证
查询 pg_indexes,并断言依赖于查找和范围扫描的列(users.email、orders.user_id、orders.created_at)确实已建立索引。请参见 references/performance-and-docker.md。
慢查询检测
填充逼真的数据量(10K+ 行),然后使用 performance.now() 测量执行时间,并断言关键查询(包含 JOIN、GROUP BY、ORDER BY 的仪表板聚合查询)在阈值内完成(例如 100ms)。
MongoDB: 使用 collection.find(...).explain('executionStats') 验证索引是否生效(stage 不得为 COLLSCAN),检查 totalDocsExamined 是否接近 nReturned,并通过 collection.indexes() 验证复合索引是否存在。
基于 Docker 的测试数据库
首选方案(2026):Testcontainers。 @testcontainers/postgresql 11.14+(2026 年 5 月)是更省事的默认选择——以编程方式管理容器生命周期、自动清理,并通过不同端口实现并行执行。它无需 docker-compose 文件,也不必管理端口冲突。PostgreSqlContainer 配置请参见 references/performance-and-docker.md。
手动编写的 compose(仍然有效): 使用 postgres:18-alpine 的 docker-compose.test.yml、用于 RAM 存储的 tmpfs,以及 pg_isready 健康检查。映射到非默认端口(例如 5433),以避免与本地 Postgres 冲突。主版本应与生产环境一致——Postgres 18 是当前版本(18.4,2026 年 5 月);除非生产环境固定使用 17,否则应升级。
在 package.json 中串联脚本:test:db:up(compose up)、test:db:migrate(prisma migrate deploy)、test:db:seed(prisma db seed)、test:db(全部运行并执行 jest)、test:db:down(compose down -v)。
反模式
1. 使用生产数据库副本进行测试
生产数据包含 PII、不具确定性且变化不可预测。请使用工厂和种子脚本生成合成数据。
2. 测试之间共享数据库状态
测试 A 插入一个用户;测试 B 假定该用户存在;CI 调整了测试顺序;测试 B 失败。请使用事务回滚或在每个测试后进行清理。
3. 忽略回滚测试
“我们从不回滚迁移”这句话一直成立,直到第一次迁移破坏生产环境。测试 down 路径。如果工具不支持回滚,这是需要记录的风险,而不是跳过测试的理由。
4. 用 migrate resolve --rolled-back 伪造回滚
该命令只会修复失败的迁移,对正常迁移则会报错。它不会还原架构。请使用真正的回滚机制:Prisma 使用 down.sql,TypeORM 使用 undoLastMigration()。
5. 使用 ORM 同步而非迁移
prisma db push、typeorm synchronize: true、Django migrate --run-syncdb 都会跳过真实的迁移路径。测试必须使用与生产环境相同的机制。
6. 只测试符合预期的查询
有数据时能返回行的查询是最简单的情况。还要测试空结果集、可选列中的 null、最大结果数量,以及针对错误数据的查询。
7. 永远不会失败的性能断言
无论索引是否存在,EXPLAIN 测试都能通过,这会造成虚假的信心。验证删除索引后测试会失败(见“验证”)。
8. 使用随机数据填充
不设固定种子的 faker.random()、uuid() 和 now() 每次运行都会生成不同的数据,导致测试具有非确定性。请使用固定种子和固定值:faker.seed(42)、明确指定的 ID、new Date('2026-01-01T00:00:00Z')。
验证
证明测试套件确实能捕获回归,从最小的检查开始:
- 干净环境下测试套件通过:
npm run test:db对全新的 Testcontainers 数据库运行时退出码为 0。 - EXPLAIN 测试确实有效: 在临时数据库中运行
DROP INDEX users_email_idx,重新运行查询性能测试,确认测试失败(规划器回退到Seq Scan),然后恢复索引并确认测试再次通过。如果删除索引后性能测试仍然通过,就说明测试有问题——请在信任它之前修复。参见references/performance-and-docker.md。 - 漂移检查能触发: 在干净的仓库中运行
prisma migrate diff --from-migrations prisma/migrations --to-schema-datamodel prisma/schema.prisma --exit-code,返回 0;手动编辑schema.prisma并确认它返回非零值。
完成标准
- 最新迁移有一个前向迁移 + 回滚测试文件:前向迁移从空数据库开始应用,并通过
information_schema断言架构;回滚时应用down.sql,断言已回滚的对象不存在,然后再次应用迁移——且该测试在 CI 中通过。 - 约束测试在
pool.query层级,对 NOT NULL、UNIQUE、FOREIGN KEY 和 CHECK 各断言一次拒绝操作(并包含数据库错误消息)。 - 数据保留测试在待测迁移之前插入行,并断言这些行在迁移后仍然存在(不要使用虚构的
--to标志)。 - 迁移漂移检查(
prisma migrate diff … --exit-code)在 CI 中运行,并在干净的仓库中以退出码 0 结束。 - 种子数据具有幂等性(
upsert+ 固定 ID),并根据SEED_ENV切换配置;重复运行两次会产生完全相同的状态。 - EXPLAIN 测试断言
Node Type与/Index/匹配,且Execution Time低于阈值,并已验证删除索引后测试会失败。 test:dbCI 任务针对 Testcontainers 数据库运行时退出码为 0(通过)。
参考文件(位于 references/ 中)
- migration-tests.md — 前向验证、通过
down.sql回滚、数据保留、漂移检测(migrate diff)和架构快照比较。 - integrity-and-seed.md — 约束和引用完整性测试、数据质量审计、工厂模式、
SEED_ENV种子脚本,以及事务回滚隔离辅助工具。 - performance-and-docker.md — EXPLAIN ANALYZE 计划断言、索引验证、删除索引有效性测试,以及 Testcontainers 设置。
相关技能
- test-data-management — 为非生产环境大规模生成和脱敏合成数据。这里介绍测试内的工厂和种子脚本;前往该技能了解大型真实数据集和 PII 脱敏。
- test-environments — 使用 Docker/IaC 配置测试数据库,并确保环境一致性。此技能在测试套件中使用 Testcontainers;test-environments 负责长期运行的基础设施。
- security-testing — SQL 注入、数据库级访问控制和加密验证。此技能测试完整性和正确性,而非对抗性输入。
- ci-cd-integration — 在 CI 流水线中运行迁移和数据库测试任务,并在 GitHub Actions 中配置测试数据库。
- performance-testing — 在并发负载下对数据库性能进行负载测试、确定连接池大小并优化查询(不在此处讨论范围内)。