在包含 TRY/CATCH 的测试过程中使用 tSQLt AssertEqualsTableSchema

Using tSQLt AssertEqualsTableSchema in a test procedure that contains TRY/CATCH

似乎 SQL 测试框架 tSQLt 不允许在生成错误的 TRY/CATCH 块之后使用诸如 tSQLt.AssertEqualsTableSchema 的断言。如果我尝试这样做,测试会生成错误而不是 pass/fail 结果。另一方面,更简单的断言如 tSQLt.AssertEquals 在这种情况下有效,通过了测试。

我想知道在这种情况下是否有办法使用这样的断言语句,或者它是否是 tSQLt 的基本限制并且它不起作用。

此代码重现了问题:

EXEC tSQLt.NewTestClass @ClassName = N'errtest';
GO

CREATE OR ALTER PROCEDURE errtest.test_simple
AS
BEGIN
    
    PRINT('Executing errtest.test_simple');

    CREATE TABLE Actual (
        A INT,
        B NVARCHAR(10));

    CREATE TABLE Expected (
        A INT,
        B NVARCHAR(10));

    BEGIN TRY
        DECLARE @IntegerVariable AS INT;
        SET @IntegerVariable = 'a string';
        PRINT('There were no errors.');
    END TRY
    BEGIN CATCH
        PRINT('An error happened.');
    END CATCH

    DECLARE @AssertType NVARCHAR(100);
    SET @AssertType = 'AssertAssertEqualsTableSchema';
    --SET @AssertType = 'AssertEquals';

    IF @AssertType = 'AssertEquals' BEGIN
        PRINT('ASSERT: Equals for 2 INTs');
        EXEC tSQLt.AssertEquals @Expected = 1
                               ,@Actual = 1
                               ,@Message = N'no'
    END 
    ELSE BEGIN
        PRINT('ASSERT: EqualsTableSchema');
        EXEC tSQLt.AssertEqualsTableSchema @Expected = N'Expected'
                                          ,@Actual = N'Actual'
                                          ,@Message = N'Hallo'
    END

END
GO


EXEC tSQLt.Run 'errtest.test_simple';

如果这是安装了 tSQLt 的 运行,我会得到以下结果:

Executing errtest.test_simple
An error happened.
ASSERT: EqualsTableSchema
[errtest].[test_simple] failed: (Error) The current transaction cannot be committed and cannot support operations that write to the log file. Roll back the transaction.[16,1]{tSQLt.AssertEqualsTableSchema,7} (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,187})
 
+----------------------+
|Test Execution Summary|
+----------------------+
 
|No|Test Case Name         |Dur(ms)|Result|
+--+-----------------------+-------+------+
|1 |[errtest].[test_simple]|      3|Error |
----------------------------------------------------------------------------------------
Msg 50000, Level 16, State 10, Line 47
Test Case Summary: 1 test case(s) executed, 0 succeeded, 0 skipped, 0 failed, 1 errored.
----------------------------------------------------------------------------------------

但是,如果我取消注释行 --SET @AssertType = 'AssertEquals';,更简单的断言 tSQLt.AssertEquals 运行s 没有错误并通过了测试:

Executing errtest.test_simple
An error happened.
ASSERT: Equals for 2 INTs
 
+----------------------+
|Test Execution Summary|
+----------------------+
 
|No|Test Case Name         |Dur(ms)|Result |
+--+-----------------------+-------+-------+
|1 |[errtest].[test_simple]|      7|Success|
----------------------------------------------------------------------------------------
Test Case Summary: 1 test case(s) executed, 1 succeeded, 0 skipped, 0 failed, 0 errored.
----------------------------------------------------------------------------------------

这里列出了在上面的示例中哪些断言有效,哪些无效。

工作:AssertEquals、AssertEqualsString、AssertNotEquals、AssertObjectDoesNotExist、AssertObjectExists、失败(未使用断言)→ 测试通过

不工作: AssertEqualsTableSchema、AssertEmptyTable、AssertEqualsTable、AssertLike、AssertResultSetsHaveSameMetaData

问题是 tSQLt 启动了一个事务——这是必需的,因此可以回滚伪造的对象并删除它创建的任何辅助对象。但是 try catch 中的错误可能会导致交易失败。 tSQLt.AssertEqualsTableSchema 尝试插入表 tSQLt.Private_AssertEqualsTableSchema_ExpectedtSQLt.Private_AssertEqualsTableSchema_Actual,这将写入日志文件,因此出现错误。

作为解决方法,您可以在不触及事务日志的情况下自行检查。下面的示例(基于 tSQLt 所做的 SELECT

  IF EXISTS (SELECT *
             FROM   (
                      SELECT 
                          C.object_id,
                          RANK()OVER(PARTITION BY C.object_id ORDER BY C.column_id) AS col_ordinal,
                          C.name,
                          CAST(C.system_type_id AS NVARCHAR(MAX))+QUOTENAME(TS.name) system_type_id,
                          CAST(C.user_type_id AS NVARCHAR(MAX))+CASE WHEN TU.system_type_id<> TU.user_type_id THEN QUOTENAME(SCHEMA_NAME(TU.schema_id))+'.' ELSE '' END + QUOTENAME(TU.name) user_type_id,
                          C.max_length,
                          C.precision,
                          C.scale,
                          C.collation_name,
                          C.is_nullable
                        FROM sys.columns AS C
                        JOIN sys.types AS TS
                          ON C.system_type_id = TS.user_type_id
                        JOIN sys.types AS TU
                          ON C.user_type_id = TU.user_type_id
                       WHERE C.object_id IN ( OBJECT_ID('dbo.Expected'), OBJECT_ID('dbo.Actual'))
                     ) T
                     GROUP BY col_ordinal, name, system_type_id, user_type_id, max_length, precision, scale, collation_name, is_nullable
                     HAVING COUNT(*) = 1
             )
  EXEC [tSQLt].Fail 'Unexpected/missing column(s)' 

显然,上面的内容可以更详细地return具体差异的更多细节,并将其本身移动到存储过程中以方便重复使用。