如何在使用tSQLt进行测试时ROLLBACK事务

时间:2012-01-23 14:26:53

标签: tsqlt

我最近在代码中调用了一个包含rasierror的过程。 raiserror陷入了困境。在raiserror之后,BEGIN TRAN也在同一个try catch块中。如果事务中发生错误,Catch块旨在ROLLBACK事务。这样做的方法是检查@@ TRANCOUNT是否大于0我知道它已经启动了一个事务并且需要ROLLBACK。当使用tSQLt进行测试时,@@ TRANCOUNT总是> 0,所以如果它遇到CATCH块,则执行ROLLBACK并且tSQLt失败(因为tSQLt在事务中运行)。当我发出错误并且运行CATCH块时,tSQLt总是无法通过测试。我无法测试正确处理raiserror的方法。您将如何创建可能会对事务进行ROLLBACK的测试用例?

4 个答案:

答案 0 :(得分:8)

如前所述,tSQLt在自己的事务中运行每个测试。跟踪正在发生的事情依赖于同一事务在测试完成时仍然打开。 SQL Server不支持嵌套事务,因此您的过程将回滚所有内容,包括为当前测试存储的框架的状态信息。那时tSQLt只能假设发生了一些非常糟糕的事情。因此,它将测试标记为错误。

如果在打开的事务中调用该过程,则SQL Server本身不会通过抛出错误来阻止过程内的回滚。有关处理此情况的方法和一些其他信息,请查看我的博文how to rollback in procedures

答案 1 :(得分: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'

答案 2 :(得分:0)

最好在BEGIN TRY之后使用BEGIN TRANSACTION阻止。当我遇到类似的问题时,我这样做了。这更合乎逻辑,因为在CATCH块中我检查了IF @@TRANCOUNT > 0 ROLLBACK。如果在BEGIN TRANSACTION之前引发了另一个错误,则不需要检查此条件。在这种情况下,您可以测试RAISERROR功能。

答案 3 :(得分: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

确认信息和代码