I am trying to write some integration tests for some SQL server stored procedures and functions. I'd like to have a database that has a known set of test data in it, and then wrap each test in a transaction, rolling it back when complete so that tests are effectively independent.
The stored procedures/functions do anything from fairly simple join queries, to complex filtering with many layers of joins, to insertion of data to multiple tables.
There are a couple stored procedures that actually use transactions - and so these are harder to test. I'll show an example of the overall code that is executed, but keep in mind this would normally be in two different spots (test setup/teardown, and the actual stored procedure). For this sample, I'm also using a very simple temp table:
CREATE TABLE #test (
val nvarchar(500)
)
Example:
-- 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
The problem here is that at "Example select #1", I would expect "aaaa" and "cccc" to be in the table, but actually only "cccc" is in the table, as SQL Server actually rolls back ALL transactions (see http://abdulaleemkhan.blogspot.com/2006/07/nested-t-sql-transactions.html). In addition, the second rollback causes an error, and although this can be avoided with:
-- begin of test teardown --
if @@trancount > 0
begin
rollback transaction
end
go
-- end of test teardown
it doesn't solve the real problem: at "Example select #2", we still get "cccc" in the table -- it no longer gets rolled back because there is no transaction active.
Is there a way around this? Is there a better strategy for this type of testing?
Note: I'm not sure if the codebase ever does do anything after rollback or not (the insert 'cccc' part) -- but if it ever does, either intentionally or accidentally, it would be possible for tests to break in strange ways as unexpected data can be left over from another test.
Somewhat similar to http://stackoverflow.com/questions/2073737/nested-stored-procedures-containing-try-catch-rollback-pattern but there is no real solution to the problem posed here.