【问题标题】:SQL Server 2012 - SP, SSDT unit test, nested transaction issueSQL Server 2012 - SP、SSDT 单元测试、嵌套事务问题
【发布时间】:2014-07-30 09:34:19
【问题描述】:

我们遇到了这样一种情况,即适用于 VS 2013 的 SSDT 在 90% 的情况下运行良好,但在测试的存储过程回滚事务时却失败了。

因为 SQL Server 并没有真正的嵌套事务,所以回滚内部事务会回滚任何外部事务。通常这不是问题,但在 SSDT 单元测试的上下文中,我们通过将单元测试方法包装在 TransactionScope / 回滚中来采取稍微定制的方法。这就是导致问题的原因,同时解决了与单元测试数据清理后测试相关的问题。

为了在我们的程序中出现需要回滚的情况时解决此问题,我们在程序结束之前添加了一个BEGIN TRANSACTION。这平衡了事务计数,并且通过我们进行的一些测试来检查前后过程 exec,它似乎确保了最后一个 BEGIN 上的隐式 COMMIT 已完成。

问题是:在正常操作下(即没有被调用中间件中的TransactionScope 包围),这似乎会引发存储过程 exec 之后的任何语句的问题同一批次。从 ORM(如 EF)或直接使用 ADO.NET 调用存储过程时是否存在问题?该语句批处理肯定会终止,留下已打开事务的隐式完成吗?

例如/引用存储过程将包含如下内容:

CREATE PROCEDURE [dbo].[ProcName]
    /* params */
AS

    SET NOCOUNT ON
    SET XACT_ABORT ON

    /* set var according to transcount check*/
    DECLARE @IsInsideTransaction BIT =  CASE WHEN @@trancount > 0 THEN 1 ELSE 0 END

    /* apply new information to data store */
    begin TRANSACTION

    declare @RetVal int = 0

    begin try
        insert into dbo.AMessageTable
            (/* some columns */)
            values
            (/* some values */)

        set @messageId = scope_identity()

        commit transaction
        set @retVal = @messageId 
    end try

    begin catch
        SELECT ERROR_NUMBER() AS ErrorNumber, ERROR_MESSAGE() AS ErrorMessage;

        SET @RetVal = -1;

        ROLLBACK TRANSACTION

        /* ### this gets us out of the transaction count issue within the UT ### */
        IF @IsInsideTransaction = 1
        BEGIN
            BEGIN TRANSACTION
        END
    end catch

    return @retVal
GO

我们修改自动生成的 SSDT 单元测试代码以包含如下内容:

[TestInitialize()]
public void TestInitialize() {
    _transaction = new TransactionScope(TransactionScopeOption.RequiresNew);

    base.InitializeTest();
}

[TestCleanup()]
public void TestCleanup() {
    base.CleanupTest();

    if (_transaction != null) {
        _transaction.Dispose();
    }
}

【问题讨论】:

    标签: sql stored-procedures transactions sql-server-2012 sql-server-data-tools


    【解决方案1】:

    在我看来你有两个问题。

    首先是您正在混合代码以使您的测试在您的生产代码中工作。这通常被视为不好的做法,因为这意味着您要么需要在生产环境中运行测试代码,要么在部署时修改存储过程——在这种情况下,您没有测试正在部署的代码,这是有风险的。

    第二个是,为了让您的测试可靠地工作,您正在创建一个奇怪的事务组合,这增加了相当多的复杂性。

    您是否查看过 tSQLt,特别是查看创建 Fakes,这意味着您创建一个假表,插入一些测试数据,然后调用您的存储过程。 tSQLt 负责回滚任何更改,因此您最终使数据库处于与测试前相同的状态,但如果您正确使用假表,那么测试前后的状态并不重要。

    【讨论】:

    • 谢谢 Ed - 我同意你的混合目的。最后,我们选择了在单元测试中管理测试数据的更简单的方法(让它负责工作)。我们没有使用其他工具或框架,而是仅使用 SSDT、VS 和 T-SQL 来管理其余部分,并结合 TFS 2013 RM。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-30
    相关资源
    最近更新 更多