我正在尝试为某些SQL Server存储过程和函数编写一些集成测试。我希望有一个数据库,其中包含一组已知的测试数据,然后将每个测试包装在一个事务中,在完成时将其回滚,以便测试实际上是独立的。
存储过程/函数可以执行任何操作,从相当简单的连接查询到具有多层连接的复杂过滤,再到将数据插入到多个表中。
有一些实际使用事务的存储过程 - 因此这些很难测试。我将展示执行的整体代码的示例,但请记住,这通常会在两个不同的位置(测试设置/拆卸,以及实际的存储过程)。对于这个示例,我还使用了一个非常简单的临时表:
CREATE TABLE #test (
val nvarchar(500)
)
示例:
-- for this example, just ensuring that the table is empty
delete from #test
go
-- begin of test setup code --
begin transaction
go
-- end of test setup code --
-- begin of code under test --
insert into #test values('aaaa')
begin transaction
go
insert into #test values('bbbbb')
rollback transaction
go
insert into #test values('ccccc')
-- Example select #1:
select * from #test
-- end of code under test --
-- begin of test teardown --
rollback transaction
go
-- end of test teardown
-- checking that #temp is still empty, like it was before test
-- Example select #2:
select * from #test
这里的问题是在“示例选择#1”中,我希望“aaaa”和“cccc”在表中,但实际上只有“cccc”在表中,因为SQL Server实际上回滚了所有交易(见http://abdulaleemkhan.blogspot.com/2006/07/nested-t-sql-transactions.html)。此外,第二次回滚会导致错误,尽管可以通过以下方式避免这种情况:
-- begin of test teardown --
if @@trancount > 0
begin
rollback transaction
end
go
-- end of test teardown
它没有解决真正的问题:在“示例选择#2”中,我们仍然在表中获得“cccc” - 它不再被回滚,因为没有活动的事务。
有解决方法吗?这种类型的测试有更好的策略吗?
注意:我不确定代码库是否在回滚后是否执行了任何操作(插入'cccc'部分) - 但如果它曾经有意或无意地执行,则测试可能会中断以奇怪的方式,因为意外的数据可以从另一个测试遗留下来。
与Nested stored procedures containing TRY CATCH ROLLBACK pattern?有些相似,但这里没有真正解决问题的方法。
答案 0 :(得分:3)
代码中的回滚不会嵌套。他们将所有内容回滚到第一个BEGIN TRANSACTION。
对于每个BEGIN TRANSACTION,@@ trancount增加1,但是,任何ROLLBACK都将@@ trancount设置为零。
如果要回滚事务的一部分,则需要使用TRANSACTION保存点。你可以在BOL中查找它们,了解更多信息,我可以在这里输入。
答案 1 :(得分:1)
我想拥有一个拥有的数据库 一组已知的测试数据,和 然后在事务中包装每个测试, 完成后回滚它 测试实际上是独立的。
别。首先,您实际上不会测试功能,因为在现实世界中,程序将提交。其次,这更重要的是,你会得到一个虚假的失败,并且需要实现变通方法来读取脏数据,因为你实际上没有提交,并且你无法进行任何适当的验证。
而是使用众所周知的设置进行数据库备份,然后在测试之前快速恢复它。将测试分组到可以全部运行在新数据库还原上的套件,而不会相互影响,因此可以减少所需的还原次数。
您还可以使用数据库快照,在套件启动时拍摄快照,然后在每次测试之前从快照还原数据库,请参阅How to: Revert a Database to a Database Snapshot (Transact-SQL)。
或者将两种方法结合起来:套件设置(即单元测试@class方法)从.bak文件恢复数据库并创建快照,然后每个测试从快照恢复数据库。
答案 2 :(得分:0)
我在这种设置方面遇到了类似的问题,我的看法是创建一个“SetupTest”脚本和一个“ClearTest”脚本,在测试执行之前和之后运行。除非你在这里谈论大量数据 - 这会使测试执行太慢,这应该可以正常工作并使测试可重复,因为你知道每次运行该测试套件时,你都会有正确的等待执行的数据。