You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现多个存储过程复用相同的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:11:16