SQL Server需用tSQLt等T-SQL框架测试存储过程,通过FakeTable隔离依赖、ResultSetFilter捕获结果并AssertEqualsTable比对;PostgreSQL可用pgTAP直接断言函数返回集;MySQL须用临时表或OUT参数抓取输出再比对;所有测试须自包含数据、统一时区、匹配环境版本与权限。

SQL Server 里怎么给存储过程写单元测试
没法直接用 xUnit 或 pytest 测试 SQL Server 的 sp_GetOrderSummary 这类存储过程——它不返回值,不抛异常,只靠 SELECT 或 OUTPUT 参数传结果。得靠外部工具或 T-SQL 自测框架,比如 tSQLt。
最轻量、最可控的做法是:在测试数据库里建一个临时 schema,用 tSQLt.NewTestClass 注册测试类,再用 tSQLt.AssertEqualsTable 对比期望结果和实际输出。
- 必须在用户数据库(非
master)中安装 tSQLt,且数据库兼容级别 ≥ 100 - 测试前用
tSQLt.FakeTable隔离依赖表,否则真实数据会让测试不可重复 - 别在测试里直接
EXEC @proc_name就完事——要捕获结果集,得用tSQLt.ResultSetFilter或临时表中转
PostgreSQL 存储函数怎么断言返回值
PostgreSQL 的函数(CREATE FUNCTION)比 SQL Server 存储过程更“函数式”,能直接返回 TABLE、SETOF 或标量值,所以可以用 SELECT 直接调用并断言。
例如测试 get_active_users() 返回 3 行,就写:
SELECT COUNT(*) FROM get_active_users() = 3;但注意:这种写法在 psql 里不会报错,也不会 fail——得靠测试驱动层(如 pgTAP)来判断真假。
- 用 pgTAP 时,
SELECT * FROM get_active_users()必须和VALUES (1),(2),(3)这种字面量做is_set()比较,不能只比行数 - 如果函数有
OUT参数或RETURNS RECORD,需显式声明AS $$ SELECT ... $$结构,否则 pgTAP 解析失败 - 事务隔离要注意:pgTAP 默认每个测试在独立事务里运行,但函数内部若含
COMMIT会直接报错ERROR: COMMIT is not allowed in a function
MySQL 存储过程没 RETURN 怎么验证逻辑
MySQL 的 CREATE PROCEDURE 不支持返回值,只能靠 OUT 参数、临时表或 SELECT 输出结果集。这意味着你没法像调函数那样“取返回值”去比较,必须把输出“抓”出来。
最稳的方式是:在测试前建一张 expected_result 表,插入预期数据;再让被测过程往 actual_result 插;最后用 SELECT COUNT(*) FROM expected_result e LEFT JOIN actual_result a ON ... WHERE a.id IS NULL 查差异。
- 避免用
SELECT * INTO OUTFILE做对比——路径权限、字符集、换行符都会导致误判 - 如果过程里用了
INSERT ... SELECT,记得在测试前后清空目标表,否则历史数据污染结果 - MySQL 8.0+ 支持 CTE 和窗口函数,但
INSERT INTO ... SELECT中不能直接嵌套 CTE,得拆成两步,否则测试 setup 失败
为什么本地跑通的测试上了 CI 就失败
常见不是代码问题,而是环境假设崩了:比如测试依赖某张表存在,但 CI 每次重建数据库,忘了跑 DDL 脚本;或者用 NOW() 做时间条件,本地和 CI 服务器时区不一致,导致 WHERE created_at > '2024-01-01' 在 UTC 环境下漏数据。
- 所有测试数据必须由测试自身初始化,不能依赖“数据库已有一条测试用户”这种隐式前提
- 涉及时间的字段,统一用
CURRENT_TIMESTAMP或固定字符串(如'2024-01-01 00:00:00'),并显式设好 session 时区:SET time_zone = '+00:00'; - 不同版本 MySQL/PostgreSQL 对空字符串、
NULL排序、隐式类型转换处理不同,CI 镜像版本必须和生产一致,不能只看大版本
最麻烦的是权限——tSQLt 要求 db_ddladmin,pgTAP 要求 CREATE on schema,而很多 CI 数据库账号只给了 CONNECT 和 USAGE。这点容易卡住一整天,却没人写在文档里。

















