如何使用 tSQLt 测试更新 table(相对于返回结果集)的 SQL 存储过程

How to test a TSQL stored procedure that updates a table (vs. returning a result set) using tSQLt

SQL专家能否插话一下测试一种存储过程的方法,这种存储过程什么都不 return,但在 table 中执行字段更新?我了解如何从函数或 SP 测试结果 returned,但是如果是就地更新,我该如何进行测试 运行假 table 与实际的 table 如果程序具有硬编码的 table 名称?我能想到的唯一方法是让整个SP使用动态SQL并将table名称作为参数传递,但这样会使代码的可读性更差且更脆弱。有没有更好的办法?下面是一个简单的存储过程,它查看另外两个字段:TransactionDate 和 EndOfDropDate,并根据条件的结果将同一 table 中名为 "IsWithinAddDrop" 的第三个字段设置为 True 或 False。

create table  tblT1
(
   ID [bigint] IDENTITY(1,1) NOT NULL,
   TransactionDate   [datetime2](7) NULL,
   EndOfDropDate      [datetime2](7) NULL, 
   IsWithinAddDrop [nvarchar](10) NULL
)

insert into tblT1 (TransactionDate, EndOfDropDate) values ('1/1/2016',  '2/1/2016')
insert into tblT1 (TransactionDate, EndOfDropDate) values ('2/1/2016',  '1/2/2016')
insert into tblT1 (TransactionDate, EndOfDropDate) values ('3/1/2016',  '3/1/2016')

create procedure spUpdateIsWithinAddDrop
as 
begin
    Update t1
        set t1.IsWithinAddDrop =
        (case
            when t1.TransactionDate <= t1.EndOfDropDate then 'True'
            else 'False'
        end)
        from tblT1 t1
end

exec spUpdateIsWithinAddDrop

结果是我要测试的 table 中的更新列 IsWithinAddDrop:

TransactionDate EndOfDropDate   IsWithinAddDrop
2016-01-01      2016-02-01      True
2016-02-01      2016-01-02      False
2016-03-01      2016-03-01      True

谢谢!

解决方案是首先模拟 table 以隔离任何依赖项(外键等)。然后添加足够的数据来测试您想要涵盖的所有情况(请参阅下面示例中的注释)并使用 tSQLt.AssertEqualsTable 将目标 table 的内容与之后的一组预定义的预期行进行比较运行 正在测试的程序。


if schema_id(N'StackModuleTests') is null
    exec tSQLt.NewTestClass @ClassName = N'StackModuleTests'
go

if objectpropertyex(object_id(N'[StackModuleTests].[test spUpdateIsWithinAddDrop example]'), N'IsProcedure') = 1
    drop procedure [StackModuleTests].[test spUpdateIsWithinAddDrop example]
go

create procedure [StackModuleTests].[test spUpdateIsWithinAddDrop example]
as
begin
    --! Start by faking the table that will be updated to isolate this test from any other dependencies
    exec tSQLt.FakeTable @TableName = 'dbo.tblT1' ;

    --! We expect spUpdateIsWithinAddDrop to set IsWithinAddDrop to TRUE only if
    --! TransactionDate is less than or equal to EndOfDropDate so we need the
    --! following tests:
    --! 
    --! Positive case where TransactionDate equals EndOfDropDate
    --! Positive case where TransactionDate less than EndOfDropDate
    --! Negative case where TransactionDate more than EndOfDropDate
    --! May want other tests to cover scenarios where either column is null
    --! Purists would say that this should one unit test for each case, personally
    --! I feel that as SQL is a set based language it is OK to combine all cases
    --! into a single test (also minimises all the setup)
    --!

    --! Assemble the data required for all test cases
    insert into tblT1 (TransactionDate, EndOfDropDate)
    values
          ('20160101', '20160101')
        , ('20160101', '20160102')
        , ('20160102', '20160101') ;

    --! What do we expect to see afterwards?
    create table #expected
    (
      TransactionDate [datetime2](7) null
    , EndOfDropDate [datetime2](7) null
    , IsWithinAddDrop [nvarchar](10) null
    )

    insert into #expected (TransactionDate, EndOfDropDate, IsWithinAddDrop)
    values
          ('20160101', '20160101', 'True')
        , ('20160101', '20160102', 'True')
        , ('20160102', '20160101', 'False') ;

    --! Act
    exec dbo.spUpdateIsWithinAddDrop ;

    --! Assert that the contents of tblT1 now match the #expected contents
    --! Notice that we ignore the ID column completely in this test because
    --! it has nothing to do with the object under test (spUpdateIsWithinAddDrop)
    exec tSQLt.AssertEqualsTable @Expected = N'#expected', @Actual = N'tblT1' ;
end
go

exec tSQLt.Run '[StackModuleTests].[test spUpdateIsWithinAddDrop example]';


希望这足以解释该方法,但如果没有,请寻求进一步的说明。