pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。
应用代码拥有快速的测试反馈循环:JavaScript、TypeScript 和 Python 都有测试运行器、测试夹具,以及从失败到通过的反馈机制。Postgres 同样可以测试,但数据库逻辑往往游离于这些流程之外。开发者需要准备数据库状态、管理事务,或者退回到粘贴查询、刷新浏览器和人工检查。这种落差会影响架构选择:如果在应用代码中测试规则更容易,规则往往就会放在那里,即使 Postgres 本来更适合执行这些约束。
模拟对象无法弥补这一落差。它们测试的是应用代码如何处理某个结果,而不是 Postgres 是否真的会产生该结果。模拟对象不会检查外键、触发触发器、执行行级安全策略,也无法验证查询实际使用的数据库角色。
pgsql-test 是采用 MIT 许可证的测试框架,将真实 PostgreSQL 数据库纳入这些反馈循环。它创建临时 PostgreSQL 数据库,只初始化一次数据,然后在每项测试结束后回滚到这一初始状态。断言使用项目现有的测试运行器执行,约束、函数和策略则由 Postgres 实际执行。它并不是第一种测试 Postgres 的方法——pgTAP 早已能够使用纯 SQL 进行测试。pgsql-test 面向的是应用层,也就是大多数开发者日常工作的地方。
行级安全(RLS)让访问规则由 Postgres 强制执行。策略属于纯数据库逻辑,模拟对象看不到它们;错误的策略可能泄露数据行。因此,测试既要检查用户能访问什么,也要检查用户不能访问什么。
pgsql-test 提供用于准备环境的管理客户端 pg,以及用于测试授权和策略的应用客户端 db。超级用户会绕过 RLS,因此测试必须使用应用实际采用的数据库角色和权限。
假设项目为 app.documents 定义了所有权策略,并通过测试夹具插入 Alice 拥有的文档 101 和 Bob 拥有的文档 202。每项测试都在事务内执行,结束后回滚,因此每次测试都从已初始化的状态开始:
```javascript import { getConnections } from 'pgsql-test';
let db, teardown;
beforeAll(async () => { // 创建全新数据库并部署项目模式 ({ db, teardown } = await getConnections()); }); afterAll(() => teardown()); beforeEach(() => db.beforeEach()); afterEach(() => db.afterEach());
test('Alice sees her document and not Bobs', async () => { db.setContext({ role: 'authenticated', 'jwt.claims.user_id': 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' });
const result = await db.query( 'SELECT id FROM app.documents ORDER BY id' );
expect(result.rows).toEqual([{ id: 101 }]); }); ```
查询没有按所有者过滤。策略必须让 Alice 的文档可见,同时让 Bob 的文档不出现在结果中。
setContext() 通过 SET LOCAL 和 set_config(..., true) 设置角色与身份信息,将其作用范围限制在当前事务内。这些设置提供策略读取的身份信息,但不会验证令牌。测试身份必须符合应用策略的预期。具体配置见 RLS 教程。
测试框架通过 pgsql-seed 初始化数据,可以加载 SQL 文件、程序化测试夹具、CSV、JSON 或迁移脚本。
同一测试框架也可以用于其他技术栈:
Supabase 首席执行官 Paul Copplestone 曾提到,一次 supabase-test 运行在四秒内完成了涉及 44 个数据库的 246 项测试。
Constructive 面向模块化 PostgreSQL 的包管理器 pgpm 可以生成包含 pgsql-test、Jest 和 GitHub Actions 的工作区;getConnections() 默认部署模块的计划。先运行 pgpm init workspace,然后添加模式变更与测试。
每项变更都包含 deploy、verify 和 revert 脚本。verify 检查模式是否已正确部署,测试则检查其行为是否符合预期。
生成的工作区通过 safegres 将这一反馈循环延伸到 CI。测试验证数据库行为是否正确,safegres 则检查已部署模式中的安全和性能退化。其 CI 作业执行安全阈值检查,并将性能检查结果与已提交的基线比较,防止变更降低模式的安全等级或增加新的性能问题。
Postgres 负责执行规则,pgsql-test 将规则的验证带入应用开发的反馈循环。