如何为PostgreSQL扩展构建完整测试体系?pg_pathman三层测试方案详解
2026/8/22 14:47:37 网站建设 项目流程

如何为PostgreSQL扩展构建完整测试体系?pg_pathman三层测试方案详解

【免费下载链接】pg_pathmanPartitioning tool for PostgreSQL项目地址: https://gitcode.com/gh_mirrors/pg/pg_pathman

pg_pathman 是一个经典的 PostgreSQL 分区工具扩展,它不仅实现了 RANGE / HASH 分区、分区自动创建等核心功能,更值得学习的是它构建的一套完整测试体系:回归测试、cmocka 单元测试与 Python 集成测试三层防线,配合 Docker 一键执行和代码覆盖率统计,堪称 PostgreSQL 扩展测试的"教科书级"范例。本文带你从零看懂这套体系是怎么搭起来的。

🧩 全景图:pg_pathman 的四层测试防线

在动手之前,先看清整个测试架构。pg_pathman 的测试由四类组成:

测试类型位置作用是否需要数据库
SQL 回归测试sql/+expected/验证 SQL 语句的实际输出需要
隔离(并发)测试specs/验证多会话并发行为需要
cmocka 单元测试tests/cmocka/纯 C 层面测试内部算法不需要
Python 集成测试tests/python/验证回归测试覆盖不到的场景需要

入口脚本run_tests.sh按顺序驱动以上全部流程,最后生成覆盖率报告。这种"分层 + 单入口"的设计,是新手搭建测试体系最值得抄的作业。

🗂️ 第一层:回归测试——用 SQL 对答案

回归测试是 PostgreSQL 扩展的"标配",pg_pathman 在 Makefile 中通过REGRESS变量注册了 30 多个测试用例,如pathman_basicpathman_insertspathman_hashjoin等。

它的工作方式非常直观:

  1. sql/目录下每个.sql文件是一段"操作脚本",比如 sql/pathman_basic.sql 会创建分区表并执行各种查询;
  2. 测试框架把每条语句在真实数据库里跑一遍;
  3. 把实际输出与expected/目录下同名的期望文件逐行对比,不一致即失败。

这种"录制-回放-对答案"的模式有三个优点:

  • 门槛低:会写 SQL 就能写测试;
  • 可读性强:期望文件本身就是一份行为文档;
  • 防回归:任何一次修改导致输出变化都会立刻暴露。

细节加分项:防止冗余期望文件

pg_pathman 还配了一个小技巧脚本 expected/test_variants.sh:PostgreSQL 支持为同一测试生成带数字后缀的多个输出文件(如xxx_1.outxxx_2.out),该脚本会自动diff这些变体,如果发现内容完全相同就报警告,防止测试文件越积越多、互相冗余。这是很多项目容易忽略的"测试卫生"细节。

⚔️ 第二层:隔离测试——把并发场景"钉"死

并发行为是分区场景的重灾区:两个事务同时插入数据、一个提交一个回滚,后台工作进程该建几个分区?这类问题用回归测试根本测不出来。

pg_pathman 使用 PostgreSQL 自带的隔离测试框架,在 specs/ 目录下放置测试脚本,例如 specs/insert_nodes.spec 定义了s1s2两个会话,每个会话拆成多个 step(开始事务、插入、回滚、查看分区),再用permutation枚举出各种执行顺序:

  • s1 回滚、s2 提交 → 验证只建出 s2 数据所需的分区;
  • 两个都回滚 → 验证分区不落地。

给新手的建议:凡是涉及"多事务 + 自动创建分区"之类的逻辑,都建议用隔离测试把关键排列组合固定下来,而不是靠人工手动试。

🧪 第三层:cmocka 单元测试——不启动数据库也能测

纯 C 写的内部算法(比如把一堆区间合并去重的 rangeset 数据结构)如果必须启动完整数据库才能测,效率极低。pg_pathman 的解法在 tests/cmocka/ 目录:

  • rangeset_tests.c 是真正的测试用例,基于 cmocka 框架编写;
  • missing_list.cmissing_bitmapset.c等文件补齐了无法直接链接进测试程序的 PostgreSQL 基础数据结构实现;
  • 编译时直接链接扩展源码里的src/rangeset.o,测试二进制rangeset_tests独立运行,完全不依赖数据库

这个目录的 Makefile 展示了关键技巧:通过pg_config --includedir-server获取头文件路径,再链接-lcmocka。新手做 C 扩展单元测试时,"把被测对象和最小依赖编译成独立程序"这个思路完全可以照搬。

🐍 第四层:Python 集成测试——补齐回归测试的盲区

有些特性靠静态 SQL 对比根本验证不了,例如:分区创建后台任务(BGW)在并发压力下是否会重复建分区、大量并发插入时的边界行为、FDW 外部分区的联动等。这些场景由 tests/python/partitioning_test.py 承担。

它基于testgres库(一个能动态创建、配置、销毁 PostgreSQL 测试实例的工具),核心套路:

  1. get_new_node()拉起临时数据库节点;
  2. 多线程 / 多进程并发执行 SQL,模拟真实压力;
  3. 用断言检查最终状态(分区数量、数据完整性、JSON 输出等)。

运行方式很简单,先安装依赖再跑单测即可:

pip3 install testgres python3 -m unittest partitioning_test

如果只想测某个特定编译版本的 PostgreSQL,设置PG_CONFIG环境变量指向对应的pg_config即可;不想测 FDW 部分时设置FDW_DISABLED=1跳过。详细说明见 tests/python/README.md。

🔁 隐藏彩蛋:升级一致性检查器

分区工具经常要处理"旧版本升级"问题。pg_pathman 在 tests/update/ 目录提供了一组工具:

  • dump_pathman_objects.sql:把扩展创建的所有对象 dump 成 SQL;
  • check_update.py:自动执行ALTER EXTENSION pg_pathman UPDATE,并对比"升级后的数据库"与"全新安装"的 dump 结果是否一致;
  • 还有pgbench压力脚本(tests/python/pgbench_scripts/)用于辅助验证。

这解决了一个很实际的问题:升级路径不会悄悄改变数据库结构,对用户来说是无感知的。

🐳 一键执行:Docker + 四级测试强度

整套测试最优雅的地方在于一键运行。docker-compose.yml 只有一行服务定义,配合 Dockerfile.tmpl 模板,可以在任意 PostgreSQL 版本的基础镜像上搭建完整测试环境(含 cmocka、valgrind、clang 静态分析等依赖)。

入口脚本run_tests.sh根据LEVEL环境变量提供四档强度,新手可以按需选择:

强度包含内容适用场景
standard编译 + 回归测试 + Python 测试 + cmocka 测试日常开发
scan-build增加 clang 静态分析(scan-build)提交前体检
hardcore重新编译带 cassert 断言的 PostgreSQL 内核深度验证
nightmare用 valgrind 内存检查跑整个数据库发布前终极考验

其中hardcore/nightmare档位会下载对应版本的 PostgreSQL 源码,对 14 及以上版本还会应用 patches/ 目录下的核心补丁(如REL_14_STABLE-pg_pathman-core.diff),重新编译出一个"带调试开关"的数据库实例再跑测试——这就是跨版本兼容性的底气来源。

覆盖率:让"测试是否充分"可量化

注意run_tests.sh中编译时始终带上了-coverage(gcov)参数,测试跑完后执行gcov生成覆盖率文件,再上传到覆盖率平台。这样每个 PR 都能直观看到"这次改动覆盖了几个百分点的代码",而不是凭感觉说"我测过了"。

💡 给新手的 4 点借鉴

  1. 分层,别贪多:SQL 能测的别上 Python,纯函数逻辑别启动数据库,每层解决自己的问题;
  2. 统一入口:一个run_tests.sh+ 一个 Docker 文件,让"跑测试"的成本降到一条命令;
  3. 测试也要做卫生:像test_variants.sh那样定期检查测试资产是否冗余;
  4. 用数据说话:接入 gcov 覆盖率统计,让测试充分程度可度量、可追溯。

pg_pathman 的这套体系(回归 + 隔离 + 单元测试 + 集成 + 升级校验 + 覆盖率)体量适中、职责清晰,非常适合作为你自己 PostgreSQL 扩展项目的测试模板。

【免费下载链接】pg_pathmanPartitioning tool for PostgreSQL项目地址: https://gitcode.com/gh_mirrors/pg/pg_pathman

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询