SQL Server 存储过程测试需捕获RETURN值、OUTPUT参数和结果集三类输出,推荐使用tSQLt框架实现事务内「准备→执行→断言→回滚」闭环,避免数据污染与依赖干扰。

SQL Server 存储过程怎么 mock 输入参数和捕获输出?
SQL Server 本身没有内置的单元测试框架,EXEC 直接调用没法隔离依赖、没法断言结果。真要测,得靠外部工具或手写验证脚本——但核心是:必须绕过事务外的副作用,且能拿到 RETURN 值、OUTPUT 参数、结果集三类输出。
常见错误是只查 SELECT 结果,漏掉 @return_code INT OUTPUT 或游标返回;或者在事务里没 ROLLBACK,导致后续测试数据污染。
- 用
DECLARE @rc INT; EXEC @rc = dbo.usp_GetUserById @id = 123;捕获返回值 -
DECLARE @name NVARCHAR(50); EXEC dbo.usp_GetUserName @id = 123, @name = @name OUTPUT;必须显式声明变量并加OUTPUT - 结果集用临时表承接:
SELECT * INTO #expected FROM (VALUES (1,'Alice')) t(id,name); SELECT * INTO #actual FROM OPENROWSET(...)不现实,改用INSERT #actual EXEC dbo.usp_ListActiveUsers;
tSQLt 是唯一靠谱的 SQL Server 单元测试方案吗?
是。开源、轻量、基于 T-SQL 实现,不依赖 .NET 或外部进程,所有断言都在数据库内执行,tSQLt.AssertEqualsTable 这类函数直接比对两个表结构+数据,连 NULL 和排序都处理得当。
容易踩的坑是安装权限:需要 db_owner 角色才能运行 tSQLt.Install();另外它强制要求每个测试用例在独立 schema(如 [test].[test_GetUserById_ReturnsActive]),否则报错 tSQLt.Private_ValidateTestClass。
- 安装后必须执行
EXEC tSQLt.NewTestClass 'test';创建测试类 schema - 测试存储过程名必须以
test_开头,且不能有参数(tSQLt会自动调用) - 用
tSQLt.FakeTable 'dbo.Users';替换真实表,避免读写生产数据 -
tSQLt.ExpectException @ExpectedMessage = N'User not found';只匹配消息子串,不是完整 error message
PostgreSQL / MySQL 怎么做等效测试?别硬套 tSQLt
PostgreSQL 用 pgTAP,MySQL 用 mysqlt 或纯 SQL 脚本,原理类似但语法差异大。关键不是“有没有框架”,而是能否在事务中完成「准备→执行→断言→回滚」闭环。
比如 PostgreSQL 中,SELECT is((SELECT status FROM users WHERE id = 1), 'active', 'user should be active'); 这种写法看着像断言,但实际执行后不会自动 rollback,必须手动包在 BEGIN; ... ROLLBACK; 里,否则测试跑完数据就留在库中了。
- pgTAP 的
plan 3;必须放在测试开头,否则finish();会报错 - MySQL 没有原生断言函数,常用
SELECT CASE WHEN COUNT(*) = 1 THEN 'PASS' ELSE 'FAIL' END FROM ...输出字符串判断,但无法抛异常中断流程 - 所有方案都避不开「结果集转成可比较结构」:要么导出为 CSV 再 diff,要么插入临时表后
EXCEPT对比
为什么不用应用层测试代替数据库层测试?
因为应用层测不到权限控制、行级安全策略(RLS)、触发器副作用、执行计划退化这些只有数据库引擎才知道的事。比如一个存储过程加了 WITH RECOMPILE,应用层调用看不出性能变化,但 sys.dm_exec_query_stats 里缓存命中率会暴跌。
最常被忽略的是时序问题:多个存储过程依赖同一张中间表,单元测试若没按依赖顺序执行,tSQLt 的 FakeTable 可能被覆盖,导致后续测试用的是空表而非预设数据。
- 测试前清空依赖表,不如用
tSQLt.ApplyConstraint临时禁用外键,再用tSQLt.CaptureOutput拦截 PRINT 输出做日志验证 - 不要在测试里写
WAITFOR DELAY '00:00:01'模拟并发——它不可靠,改用tSQLt.SpiedOn监控被调用次数更准 - 上线前必须跑一次带真实统计信息的集成测试,单元测试再全也 cover 不了参数嗅探引发的执行计划偏差

















