如何实现多个存储过程复用相同的tSQLt通用测试?
当然可以!用tSQLt实现通用测试的复用完全可行
我之前也遇到过一模一样的问题——维护十几个测试类里重复的通用测试简直是噩梦。后来通过把通用测试逻辑封装成可复用的存储过程,再让每个测试类调用的方式完美解决了这个问题。下面是具体的实现思路和代码示例:
核心思路
把所有通用测试逻辑集中到一个通用测试类的存储过程里,这些存储过程接受目标存储过程的名称作为参数(或者通过测试类名称自动推导),然后在每个具体的测试类中创建轻量的测试方法,直接调用通用测试逻辑,让测试自动针对当前类对应的存储过程执行。
步骤1:创建通用测试类与通用测试存储过程
首先创建一个专门存放通用测试的测试类,比如Test_Common,然后在里面实现所有通用的测试逻辑,每个测试存储过程接受目标存储过程名称作为参数:
-- 创建通用测试类 EXEC tSQLt.NewTestClass 'Test_Common'; GO -- 通用测试示例1:验证存储过程拒绝必填参数为NULL CREATE PROCEDURE [Test_Common].[test_Common_RejectsNullRequiredParameters] @TargetProc NVARCHAR(128) AS BEGIN SET NOCOUNT ON; -- 动态生成调用目标存储过程的语句(用QUOTENAME避免SQL注入) DECLARE @ExecSql NVARCHAR(MAX) = N'EXEC ' + QUOTENAME(@TargetProc) + N' @RequiredParam = NULL;'; BEGIN TRY EXEC sp_executesql @ExecSql; -- 如果没有抛出错误,测试失败 EXEC tSQLt.Fail '存储过程未拦截必填参数的NULL值'; END TRY BEGIN CATCH -- 验证错误信息符合预期 EXEC tSQLt.AssertLike @ExpectedPattern = '%必填参数不能为空%', @Actual = ERROR_MESSAGE(), @Message = '错误提示不符合通用规则'; END CATCH END; GO -- 通用测试示例2:验证存储过程返回正确的结果集结构 CREATE PROCEDURE [Test_Common].[test_Common_ReturnsCorrectResultStructure] @TargetProc NVARCHAR(128) AS BEGIN SET NOCOUNT ON; -- 执行目标存储过程并捕获结果集 CREATE TABLE #ActualResults ( Id INT, Name NVARCHAR(50), CreatedDate DATETIME ); INSERT INTO #ActualResults EXEC @TargetProc @FilterParam = 'Test'; -- 验证结果集列数与类型 EXEC tSQLt.AssertResultSetsHaveSameMetaData @Expected = N'(Id INT, Name NVARCHAR(50), CreatedDate DATETIME)', @Actual = N'#ActualResults'; END; GO
步骤2:在具体测试类中调用通用测试
接下来,在每个对应存储过程的测试类里,创建简单的测试方法,直接调用通用测试存储过程,并传入当前测试类对应的存储过程名称:
-- 针对存储过程dbo.ProcA的测试类 EXEC tSQLt.NewTestClass 'Test_ProcA'; GO -- 调用通用测试:验证ProcA拒绝NULL必填参数 CREATE PROCEDURE [Test_ProcA].[test_RejectsNullRequiredParameters] AS BEGIN EXEC [Test_Common].[test_Common_RejectsNullRequiredParameters] @TargetProc = 'dbo.ProcA'; END; GO -- 调用通用测试:验证ProcA返回正确的结果集结构 CREATE PROCEDURE [Test_ProcA].[test_ReturnsCorrectResultStructure] AS BEGIN EXEC [Test_Common].[test_Common_ReturnsCorrectResultStructure] @TargetProc = 'dbo.ProcA'; END; GO
进阶优化:自动推导目标存储过程名称
如果你的测试类命名遵循Test_<存储过程名>的规则(比如Test_ProcA对应dbo.ProcA),可以在通用测试里自动推导目标存储过程名称,省去每次传参的麻烦:
修改通用测试的参数部分,自动获取当前调用的测试类名称并推导:
ALTER PROCEDURE [Test_Common].[test_Common_RejectsNullRequiredParameters] AS BEGIN SET NOCOUNT ON; -- 从当前测试类名称推导目标存储过程名 DECLARE @TestClassName NVARCHAR(128) = tSQLt.GetCurrentTestClass(); DECLARE @ProcName NVARCHAR(128) = SUBSTRING(@TestClassName, 6, LEN(@TestClassName) - 5); -- 截取Test_后面的部分 DECLARE @TargetProc NVARCHAR(128) = 'dbo.' + @ProcName; -- 后续逻辑和之前一致 DECLARE @ExecSql NVARCHAR(MAX) = N'EXEC ' + QUOTENAME(@TargetProc) + N' @RequiredParam = NULL;'; -- ... 省略其余测试逻辑 END; GO
现在具体测试类里的调用就更简单了:
CREATE PROCEDURE [Test_ProcA].[test_RejectsNullRequiredParameters] AS BEGIN -- 无需传参,通用测试自动推导目标存储过程 EXEC [Test_Common].[test_Common_RejectsNullRequiredParameters]; END; GO
注意事项
- 动态SQL安全:一定要用
QUOTENAME()处理存储过程名称,避免SQL注入风险。 - 特殊情况处理:如果某个存储过程需要特殊的通用测试逻辑,可以在对应的测试类里重写该测试方法,覆盖通用逻辑。
- 测试隔离:tSQLt会为每个测试方法创建独立的事务,所以通用测试的操作不会影响其他测试的执行环境。
- 可读性:保持具体测试类里的测试方法名称和通用测试一致,这样在测试结果里能清晰看到每个存储过程的通用测试执行情况。
内容的提问来源于stack exchange,提问作者christiaantober
相关产品推荐
相关产品推荐

