TSQLT 测试 运行 超时

TSQLT Test Run Timeout

我最近开始使用 TSQLT 通过 Redgate 的 SQL 测试来创建和 运行 单元测试。不过我 运行 遇到了问题。执行时间超过几分钟的单元测试将超时,从而停止执行所有其他单元测试。

如何延长 tSQLt 的超时长度?

我的 "unit testing" 可能不是真正的单元测试,但我不熟悉另一种更适合的测试方法。

我正在开展一个项目,以提高我们数据仓库的夜间刷新速度。目前,这个过程需要五个小时。通过尽可能并行地将任务重新安排到 运行,我已将时间缩短到两个小时。我的问题是,除非我能找到一种方法来证明新流程与旧流程具有完全相同的最终结果,否则 QA 将在明年检查每个 table 中每一行每一列的每个值。否则项目将被废弃为 "too hard".

所以我想出的测试是这样的: 我有一个数据库,其中我 运行 一个脚本,用于在使用我创建的新方法在我们的测试环境中处理后复制结果 tables。然后,回到测试环境,我 运行 旧进程更新 tables。然后,我 运行 每个 table 一个单元测试,以证明使用新方法处理的存档 tables 的内容与 tables 的内容完全相同使用旧方法重新处理。

不幸的是,由于其中一些 table 的大小(数百万行),一些单元测试正在超时。以下是我收到的错误:

Test Procedure: [HR360_unitTest1].[HR360_DW_Job6].[test fact_group_clients Identical Contents] on emr\preprod System.Data.SqlClient.SqlException (0x80131904): Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding. ---> System.ComponentModel.Win32Exception (0x80004005): The wait operation timed out at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction) at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction) at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose) at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady) at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString) at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite, SqlDataReader ds, Boolean describeParameterEncryptionRequest) at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, TaskCompletionSource1 completion, Int32 timeout, Task& task, Boolean asyncWrite) at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource1 completion, String methodName, Boolean sendToPipe, Int32 timeout, Boolean asyncWrite) at System.Data.SqlClient.SqlCommand.ExecuteNonQuery() at RedGate.SQLTest.tSQLt.FrameworkWrapper.#kz(SqlCommand #LGj) at RedGate.SQLTest.tSQLt.FrameworkWrapper.#7qHc(String #2xAd, SqlParameter[] #LvPb) at RedGate.SQLTest.tSQLt.FrameworkWrapper.#qd4b(String #LGxc) at RedGate.SQLTest.tSQLt.TestRunner.Execute(SqlConnection connection) ClientConnectionId:519569ed-03ce-4510-b226-9ff18e0f1d8d Error Number:-2,State:0,Class:11

如果没有办法增加 tSQLt 的超时时间,那么我要么必须找到另一种方法来自动测试这些 tables 的内容是否相同(在一种随意重复table的方式)或放弃该项目。

tSQLt 可能不是我进行此类测试的首选,它专为单元测试而设计,并且在单元测试方面表现出色——通常是对小部分代码的小型、快速测试。您可以查看 DbFit 之类的内容,但这也会与该数据量作斗争。

如果您真的必须证明每个 table 上的每一行的每一列都是相同的,我会考虑在两个数据库中的每个 table 上创建一个 MD5 哈希列,然后查询每个 table 对于散列不匹配的行。在构建哈希值时,您需要排除任何您不能保证在两个 table 中相同的列,例如IDENTITY 值,加载 date/times 等。例如:

——!创建我们现有的 table 结构 创建 table dbo.ExistingCustomer ( CustomerId int not null identity(1,1) 主键 , 姓氏 varchar(50) 不为空 , FirstName varchar(50) 不为空 , 中间名 varchar(50) 空 , TownOfBirth varchar(50) 空 , DateOfBirth 日期时间不为空 , NumberOfDependents int 不为空 , EtlCreatedOn 日期时间不为空 ) 去</p> <p>——!添加一个连接所有感兴趣的列的计算列 ——!成一个字符串,处理过程中的 NULL,然后创建 ——!对整个字符串进行 32 个字符的 MD5 散列 改变 table dbo.ExistingCustomer 添加 DeltaHash 作为 转换(nvarchar(32),hashbytes('MD4' ,转换(nvarchar(最大) ——!即使您可能合理地期望不会填充名字和姓氏, ——!始终防御性编码对一系列空字符串进行散列将给出 ——!不太自信的结果 , coalesce(nullif(LastName, ''), 'LastName') + coalesce(nullif(FirstName, ''), 'FirstName') ——!此模式对可为空的列也有效 + coalesce(nullif(MiddleName, ''), 'MiddleName') + coalesce(convert(char(24), TownOfBirth, 121), 'TownOfBirth') + coalesce(cast(NumberOfDependents as varchar(32)), 'NumberOfDependents')) collat​​e Latin1_General_CI_AS), 2) 坚持 走

构建此散列时,您需要非常小心排序规则(在两个数据库之间,甚至在列级别)和空格。假设您在一行中有四个整数列,所有列都是 NULL 栏。如果您仅将 null 替换为空字符串,则无论第二列还是第三列是否包含有效整数,MD5 哈希都将相同。

您需要将此列添加到旧 table 和新加载数据的副本中,然后您可以使用这样的查询:

--! Expect Zero select count(*) as [FailCount] from OldLoadDb.dbo.ExistingCustomer as ec inner join NewLoadDb.dbo.NewCustomer as nc --! This join should probably be on some business key but you get the idea on nc.CustomerId = ec.CustomerId where ec.DeltaHash <> ec.DeltaHash go

像上面这样的东西在 DbFit 中会 运行 非常好,因为所有 g运行t 工作都在服务器端完成。

您还应该为一个 table 而另一个

中存在的行添加测试。

当然,哈希查询不会告诉您差异是什么,但至少可以让您识别不同的行。

超时问题原来是在 Redgate SQL 测试中。因此,长 运行ning 单元测试的简单解决方案是通过调用 tSQLt.Run 在 tSQLt 框架中直接 运行 它们。在我的测试中,tSQLt 似乎没有超时问题。在撰写本文时,我有一个单元测试已经 运行 连续 19 个小时没有超时。

这个单元测试花了很长时间 运行,在我的特殊情况下产生了它自己的问题。我将使用散列解决方案或 EXCEPT 解决方案来解决这个问题,正如在@datacentricity 的回复中所讨论的那样。