2010-02-22 107 views
0

我正在嘗試爲某些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?但這裏提出的問題沒有真正的解決方案。

回答

3

代碼中的回滾不會嵌套。他們將所有內容回滾到第一個BEGIN TRANSACTION。

對於每個BEGIN TRANSACTION,@@ trancount會增加1,但是,任何ROLLBACK都會將@@ trancount設置回零。

如果要回滾事務的一部分,則需要使用TRANSACTION保存點。你可以看看他們在BOL,更多的信息比我可以在這裏輸入。

http://msdn.microsoft.com/en-us/library/ms188378.aspx

+0

這樣做的一個問題是它需要修改內部異常 - 也就是被測代碼(存儲過程)中的一個。如果有人在代碼庫中編寫事務時不使用這種模式,它仍然可能以不可預知的方式破壞測試。如果可以修改外部事務(測試框架的一部分)以使其適用於在正在測試的代碼中編寫的「常規」事務,那將會更好。不過謝謝,否則這是一件很好的事情要知道。 – gregmac 2010-02-22 22:40:53

+1

@gregmac,TSQL就是這樣。我正確地解釋了爲什麼你的代碼有問題,以及你有什麼選擇。 – 2010-02-23 12:46:44

1

我想有一個具有 一組已知的測試數據中有一個數據庫,然後 包裝每個測試在一個事務中, 回滾完成時,使 測試是有效的獨立。

不要。首先,你不會真正測試這個功能,因爲在現實世界中,這些過程將會提交。其次,這是非常重要的,你會得到一個錯誤的失敗,並且需要實現解決方法來讀取髒數據,因爲你實際上沒有提交,並且你不能做任何適當的驗證。

取而代之的是具有衆所周知的數據庫備份,然後在測試之前快速恢復它。將測試分組到各個套件中,這些套件可以在全新數據庫恢復時運行,而不會相互影響,因此可以減少所需的恢復次數。

您還可以使用數據庫快照,爲套件啓動創建快照,然後在每次測試之前從快照中恢復數據庫,請參閱How to: Revert a Database to a Database Snapshot (Transact-SQL)

或者結合這兩種方法:suite setup(即unit test @class method)從.bak文件恢復數據庫並創建一個快照,然後每個測試都從快照中恢復數據庫。

+0

我應該更清楚地瞭解這個設置:實際的測試斷言將在測試結束和拆解之前完成 - 因此在這一點上,數據將被提交。 – gregmac 2010-02-22 19:48:52

+0

數據無法提交併在以後回滾。那時數據沒有被提交。 – 2010-02-22 23:10:08

0

我有類似的問題與那種設置和我承擔這是創建一個「SetupTest」腳本和「ClearTest」腳本運行前後執行測試。除非你在這裏談論大量的數據 - 這會使測試執行速度太慢,這應該很好,並使測試可重複,因爲你知道每次運行測試套件時,你都會得到正確的等待執行的數據。