tSQLt 在 SQL Server 2017 AG 上启用 DTC

tSQLt on SQL Server 2017 AG with DTC enabled

当我在 运行 tSQLt 测试 SQL 服务器 2017 时收到以下错误,该服务器设置在配置为 [=19 的 Always On 可用性组中=] = PER_DB 上AG组。如果我关闭 DTC 支持,它工作正常。我们的环境需要 DTC。有没有办法在 tSQLt 中解决这个问题,或者是否有不同的方法来配置 SQL 以便 DTC 和 tSQLt 都可以工作?

这里是错误:

Test Procedure: [xxx].[zzfnCipValidateCustomerAddress].[test '12345' Zip Codes] on DbServer
[zzfnCipValidateCustomerAddress].[test '12345' Zip Codes] failed: (Error) Cannot promote the transaction to a distributed transaction because there is an active save point in this transaction.[16,1]{zzfnCipValidateCustomerAddress.test '12345' Zip Codes,36} (There was also a ROLLBACK ERROR --> The current transaction cannot be committed and cannot be rolled back to a savepoint. Roll back the entire transaction.{tSQLt.Private_RunTest,160})

这是失败的测试之一:

CREATE PROCEDURE [zzfnCipValidateCustomerAddress].[test '12345' Zip Codes]
AS
BEGIN
--Assemble
/*
Test to determine if function is an Inline Table Valued function or not
since this function is being rewritten to be an ITVF function and this test should
pass both versions
*/
DECLARE @IsITVF BIT;

SELECT @IsITVF = IIF(fc.FunctionCount > 0, 1, 0)
FROM
(
    SELECT COUNT(*) AS FunctionCount
    FROM sys.sql_modules AS sm
        JOIN sys.objects AS o
            ON sm.object_id = o.object_id
    WHERE sm.object_id = OBJECT_ID('dbo.fnCipValidateCustomerAddress')
          AND o.type = 'IF' --'IF' = Inline Valued Table Function
) AS fc;

SELECT @IsITVF;


--DROP TABLE IF EXISTS zzfnCipValidateCustomerAddress.TestData;

CREATE TABLE zzfnCipValidateCustomerAddress.TestData
(
    AddressLine1 VARCHAR(100),
    AddressLine2 VARCHAR(100),
    City VARCHAR(50),
    StateAbbr VARCHAR(3),
    ZipCode VARCHAR(9)
);

INSERT INTO zzfnCipValidateCustomerAddress.TestData
(
    AddressLine1,
    AddressLine2,
    City,
    StateAbbr,
    ZipCode
)
VALUES
('Test', NULL, 'Test', 'AZ', '12345');

CREATE TABLE zzfnCipValidateCustomerAddress.Expected
(
    AddressLine1 VARCHAR(100),
    AddressLine2 VARCHAR(100),
    City VARCHAR(50),
    StateAbbr VARCHAR(3),
    ZipCode VARCHAR(9),
    CipExceptionReasonId INT
);

INSERT INTO zzfnCipValidateCustomerAddress.Expected
(
    AddressLine1,
    AddressLine2,
    City,
    StateAbbr,
    ZipCode,
    CipExceptionReasonId
)
VALUES
('Test', NULL, 'Test', 'AZ', '12345', 8);

--Act

IF (@IsITVF = 0)
BEGIN
    SELECT td.AddressLine1,
           td.AddressLine2,
           td.City,
           td.StateAbbr,
           td.ZipCode,
           dbo.fnCipValidateCustomerAddress(td.AddressLine1, td.AddressLine2, td.City, td.StateAbbr, td.ZipCode) AS CipExceptionReasonId
    INTO zzfnCipValidateCustomerAddress.Actual
    FROM zzfnCipValidateCustomerAddress.TestData AS td;
END;
ELSE
BEGIN
    SELECT fcvcat.AddressLine1,
           fcvcat.AddressLine2,
           fcvcat.City,
           fcvcat.StateAbbr,
           fcvcat.ZipCode,
           fcvcat.CipExceptionReasonId
    INTO zzfnCipValidateCustomerAddress.Actual
    FROM zzfnCipValidateCustomerAddress.TestData AS td
        CROSS APPLY dbo.fnCipValidateCustomerAddress(
                                                        td.AddressLine1,
                                                        td.AddressLine2,
                                                        td.City,
                                                        td.StateAbbr,
                                                        td.ZipCode
                                                    ) AS fcvcat;
END;


--Assert

EXEC tSQLt.AssertEqualsTable @Expected = N'zzfnCipValidateCustomerAddress.Expected',
                             @Actual = N'zzfnCipValidateCustomerAddress.Actual',
                             @Message = N'',
                             @FailMsg = N'Zip with value of ''12345'' did not generate a CipExceptionReasonId as expected';

END;

tSQLt 内部依赖事务保存点。保存点与分布式事务不兼容。

目前在 tSQLt 中无法更改事务处理。

我的建议是设置一个专用的 CI(持续集成)环境,专门用于执行您的自动化测试套件(因此不需要启用 AlwaysOn)。无论您的具体情况如何,这都是行业最佳实践。

但是,这并不能解决专门处理您的 AlwaysOn 设置的测试过程。您自然必须在启用 AlwaysOn 的环境中测试的那些。在这种情况下,您可以使用 tSQLt.NewConnection 命令执行在单独的连接上传入的命令以及 tSQLt 事务之外的命令。

不过请记住,在这种情况下,您将负责所有清理操作,因为通过 tSQLt.NewConnection 执行的任何操作显然不会被 tSQLt 回滚。