2012-01-23 86 views
4

我最近在調用包含代碼中的一個rasierror的過程。 raiserror在try catch塊中。另外一個BEGIN TRAN在raiserror之後也在同一個try catch塊中。 Catch塊設計用於在事務發生錯誤時對事務進行ROLLBACK。這樣做的方式是檢查@@ TRANCOUNT是否大於0我知道它已經啓動了一個事務並需要ROLLBACK。使用tSQLt進行測試時,@@ TRANCOUNT始終> 0,因此如果它遇到CATCH塊,則會執行ROLLBACK並且tSQLt失敗(因爲tSQLt正在事務中運行)。當我發現一個錯誤並且運行CATCH塊時,tSQLt總是不能通過測試。我無法測試正確處理raiserror的情況。你將如何創建一個可能會ROLLBACK事務的測試用例?如何在使用tSQLt進行測試時回滾事務

+0

的可能重複[需要回滾如果查詢完成有錯誤?(http://stackoverflow.com/questions/8991207/is-rollback-needed-if-query-completed-with-errors) – Dijkgraaf

回答

8

正如您所提到的,tSQLt在自己的事務中運行每個測試。跟蹤發生的事情依賴於同一事務在測試完成時仍然處於打開狀態。 SQL Server不支持嵌套事務,因此您的過程將回滾所有內容,包括框架爲當前測試存儲的狀態信息。那時tSQLt只能認爲發生了一些非常糟糕的事情。因此它將測試標記爲錯誤。

如果在打開的事務中調用該過程,SQL Server本身會阻止在過程內部進行回滾。有關如何處理這種情況和一些額外的信息請查看我的博客文章how to rollback in procedures

0

最好在BEGIN TRANSACTION之後使用BEGIN TRY塊。當我遇到類似問題時,我做了這個。這更合乎邏輯,因爲在CATCH區塊我檢查了IF @@TRANCOUNT > 0 ROLLBACK。如果在BEGIN TRANSACTION之前發生另一個錯誤,則無需檢查此情況。在這種情況下,您可以測試您的RAISERROR功能。

0

+1以上兩個答案。

但是,如果您不想使用TRY .. CATCH,請嘗試以下代碼。行-----之間的部分代表測試,並且在代表tSQLt的之前和之後調用您的測試。正如你所看到的,在調用測試之前,由tSQLt開始的事務仍然在,正如它所期望的那樣,是否發生錯誤。 @@ TRANSCOUNT仍然是1

您可以將RAISERROR註釋掉,並在不引發異常的情況下嘗試使用它。

SET NOCOUNT ON 

BEGIN TRANSACTION -- DONE BY tSQLt 
PRINT 'Inside tSQLt before calling the test: @@TRANCOUNT = ' + CONVERT (VARCHAR, @@TRANCOUNT) 

    --------------------------------- 
    PRINT ' Start of test ---------------------------' 

    SAVE TRANSACTION Savepoint 
    PRINT ' Inside the test: @@TRANCOUNT = ' + CONVERT (VARCHAR, @@TRANCOUNT) 


    BEGIN TRANSACTION -- PART OF THE TEST 
    PRINT ' Transaction in the test: @@TRANCOUNT = ' + CONVERT (VARCHAR, @@TRANCOUNT) 

     RAISERROR ('A very nice error', 16, 0) 

     PRINT ' @@ERROR = ' + CONVERT(VARCHAR,@@ERROR) 


    -- PART OF THE TEST - CLEAN-UP 
    IF @@ERROR <> 0 ROLLBACK TRANSACTION Savepoint -- Not all the way, just tothe save point 
    ELSE COMMIT TRANSACTION 

    PRINT ' About to finish the test: @@TRANCOUNT = ' + CONVERT (VARCHAR, @@TRANCOUNT) 

    PRINT ' End of test ---------------------------' 

    --------------------------------- 

ROLLBACK TRANSACTION -- DONE BY tSQLt 
PRINT 'Inside tSQLt after finishing the test: @@TRANCOUNT = ' + CONVERT (VARCHAR, @@TRANCOUNT) 

有了確認信息和代碼在http://www.blackwasp.co.uk/SQLSavepoints.aspx

2

正如我只是讀了上tSQLt這是的浮現在腦海的第一個問題時,我已經學會在交易的每個測試運行。由於我的一些存儲過程確實啓動了事務處理,有些甚至使用嵌套事務處理,這可能會變得很有挑戰性我已經瞭解到嵌套事務,如果你應用以下規則,你可以保持你的代碼清理常量錯誤檢查,並且仍然適度地處理錯誤。

  • 打開交易時,始終使用try/catch塊
  • 始終提交事務,除非誤差升至
  • 始終回滾事務時被升高除非@@ TRANCOUNT = 0
  • 錯誤除非您確定在存儲過程開始時沒有打開任何事務,否則請始終重新評估錯誤。

保持這些規則在這裏是一個proc實現和測試代碼的例子來測試它。

ALTER PROC testProc 
    @IshouldFail BIT 
AS 
BEGIN TRY 
    BEGIN TRAN 

    IF @IshouldFail = 1 
     RAISERROR('failure', 16, 1); 

    COMMIT 
END TRY 
BEGIN CATCH 
    IF @@TRANCOUNT > 0 
     ROLLBACK; 

    -- Do some exception handling 

    -- You'll need to reraise the error to prevent exceptions about inconsistent 
    -- @@TRANCOUNT before/after execution of the stored proc. 
    RAISERROR('failure', 16, 1); 
END CATCH 
GO 


--EXEC tSQLt.NewTestClass 'tSQLt.experiments'; 
--GO 
ALTER PROCEDURE [tSQLt.experiments].[test testProc nested transaction fails] 
AS 
BEGIN 
    --Assemble 
    DECLARE @CatchWasHit CHAR(1) = 'N'; 

    --Act 
    BEGIN TRY 
     EXEC dbo.testProc 1 
    END TRY 
    BEGIN CATCH 
     IF @@TRANCOUNT = 0 
      BEGIN TRAN --reopen an transaction 
     SET @CatchWasHit = 'Y'; 
    END CATCH 

    --Assert 
    EXEC tSQLt.AssertEqualsString @Expected = N'Y', @Actual = @CatchWasHit, @Message = N'Exception was expected' 

END; 
GO 

ALTER PROCEDURE [tSQLt.experiments].[test testProc nested transaction succeeds] 
AS 
BEGIN 
    --Act 
    EXEC dbo.testProc 0 

END; 
GO 

EXEC tSQLt.Run @TestName = N'tSQLt.experiments' 
相關問題