最近、コードに rasierror を含むプロシージャを呼び出していました。レイザーエラーは try catch ブロックにありました。また、BEGIN TRAN は、raiserrr の後の同じ try catch ブロックにありました。Catch ブロックは、トランザクションでエラーが発生した場合にトランザクションを ROLLBACK するように設計されています。これを行う方法は、@@TRANCOUNT が 0 より大きいかどうかを確認することです。トランザクションが開始され、ロールバックする必要があることがわかっています。tSQLt でテストする場合、@@TRANCOUNT は常に >0 であるため、CATCH ブロックにヒットすると ROLLBACK が実行され、tSQLt は失敗します (tSQLt がトランザクションで実行されているため)。エラーが発生し、CATCH ブロックが実行されると、tSQLt は常にテストに失敗します。レイザーエラーの正しい処理をテストする方法がありません。トランザクションをロールバックする可能性のあるテスト ケースをどのように作成しますか?
4 に答える
あなたが述べたように、tSQLtはそれ自身のトランザクションですべてのテストを実行します。何が起こっているかを追跡することは、テストが終了したときにまだ開いている同じトランザクションに依存しています。SQL Serverはネストされたトランザクションをサポートしていないため、現在のテスト用にフレームワークに格納されているステータス情報を含め、プロシージャはすべてをロールバックします。その時点で、tSQLtは本当に悪いことが起こったとしか想定できません。したがって、テストにエラーのマークが付けられます。
SQL Server自体は、開いているトランザクション内でプロシージャが呼び出された場合にエラーをスローすることにより、プロシージャ内でのロールバックを阻止します。この状況に対処する方法といくつかの追加情報については、手順をロールバックする方法についての私のブログ投稿を確認してください。
tSQLt について読んでいるときに、これは、各テストがトランザクションで実行されることを知ったときに頭に浮かんだ最初の質問の 1 つです。私のストアド プロシージャの中にはトランザクションを開始するものもあれば、ネストされたトランザクションを使用するものもあるため、これは困難になる可能性があります。ネストされたトランザクションについて私が学んだことは、次のルールを適用すると、コードを一定のエラー チェックから解放し、エラーを適切に処理することができるということです。
- トランザクションを開くときは常に 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'
BEGIN TRY
の後にブロックを使用することをお勧めしますBEGIN TRANSACTION
。同様の問題が発生したときにこれを行いました。CATCH
ブロックでチェックしたので、これはより論理的IF @@TRANCOUNT > 0 ROLLBACK
です。の前に別のエラーが発生した場合、この条件をチェックする必要はありませんBEGIN TRANSACTION
。この場合、RAISERROR
機能をテストできます。