2012-01-23 44 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' 
相关问题