我最近在代码中调用了一个包含rasierror的过程。 raiserror陷入了困境。在raiserror之后,BEGIN TRAN也在同一个try catch块中。如果事务中发生错误,Catch块旨在ROLLBACK事务。这样做的方法是检查@@ TRANCOUNT是否大于0我知道它已经启动了一个事务并且需要ROLLBACK。当使用tSQLt进行测试时,@@ TRANCOUNT总是> 0,所以如果它遇到CATCH块,则执行ROLLBACK并且tSQLt失败(因为tSQLt在事务中运行)。当我发出错误并且运行CATCH块时,tSQLt总是无法通过测试。我无法测试正确处理raiserror的方法。您将如何创建可能会对事务进行ROLLBACK的测试用例?
答案 0 :(得分:8)
如前所述,tSQLt在自己的事务中运行每个测试。跟踪正在发生的事情依赖于同一事务在测试完成时仍然打开。 SQL Server不支持嵌套事务,因此您的过程将回滚所有内容,包括为当前测试存储的框架的状态信息。那时tSQLt只能假设发生了一些非常糟糕的事情。因此,它将测试标记为错误。
如果在打开的事务中调用该过程,则SQL Server本身不会通过抛出错误来阻止过程内的回滚。有关处理此情况的方法和一些其他信息,请查看我的博文how to rollback in procedures。
答案 1 :(得分:2)
由于我刚刚阅读了tSQLt,这是我在交易过程中了解到每个测试后首先想到的问题之一。由于我的一些存储过程确实启动了事务,有些甚至使用嵌套事务,这可能变得具有挑战性。我已经了解了嵌套事务,如果你应用以下规则,你可以保持代码清理不断的错误检查,并仍然优雅地处理错误。
这里记住这些规则是一个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)
确认信息和代码