我最近开始使用TSQLT通过Redgate的SQL测试来创建和运行单元测试。我遇到了一个问题。执行时间超过几分钟的单元测试将超时,这将暂停所有其他单元测试的执行。
如何延长tSQLt超时的长度?
我的"单元测试"实际上可能不是单元测试,但我不熟悉另一种更适合的测试方法。
我正在开展一个项目,以提高每晚刷新数据仓库的速度。目前,这个过程需要五个小时。通过在可能的情况下重新安排任务并行运行,我将时间减少到两个小时。我的问题是,除非我能找到一种方法来证明新进程与旧进程具有完全相同的最终结果,否则QA将在下一年检查每个表中每一行的每一列中的每个值。无论是那个还是项目都将被废弃为“太难了”#34;
所以我提出的测试是这样的: 我有一个数据库,在我使用我创建的新方法在我们的测试环境中处理它们之后运行脚本来复制结果表。然后,回到测试环境,我运行旧进程来更新表。然后,我对每个表运行一个单元测试,以证明使用新方法处理的存档表的内容与使用旧方法重新处理的表的内容完全相同。
不幸的是,由于其中一些表(数百万行)的大小,一些单元测试正在超时。以下是我收到的错误:
测试程序:[HR360_unitTest1]。[HR360_DW_Job6]。[test emr \ preprod上的fact_group_clients相同内容] System.Data.SqlClient.SqlException(0x80131904):超时已过期。该 在完成操作或之前经过的超时时间 服务器没有响应。 ---> System.ComponentModel.Win32Exception (0x80004005):等待操作超时 System.Data.SqlClient.SqlConnection.OnError(SqlException异常, 布尔值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,布尔& 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超时,任务& task,Boolean asyncWrite,SqlDataReader ds,Boolean describeParameterEncryptionRequest)at System.Data.SqlClient.SqlCommand.RunExecuteReader(的CommandBehavior cmdBehavior,RunBehavior runBehavior,Boolean returnStream,String 方法,TaskCompletionSource
1 completion, Int32 timeout, Task& task, Boolean asyncWrite) at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource
1 completion,String methodName,Boolean sendToPipe,Int32 timeout, 布尔asyncWrite)at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()at RedGate.SQLTest.tSQLt.FrameworkWrapper。#kz(SqlCommand #LGj)at at RedGate.SQLTest.tSQLt.FrameworkWrapper。#7qHc(String#2xAd, SqlParameter [] #LvPb)at RedGate.SQLTest.tSQLt.FrameworkWrapper。#qd4b(String #LGxc)at at RedGate.SQLTest.tSQLt.TestRunner.Execute(SqlConnection连接) ClientConnectionId:519569ed-03ce-4510-b226-9ff18e0f1d8d错误 号:-2,状态:0,类别:11
如果没有办法增加tSQLt的超时,那么我要么必须找到另一种方法来自动测试这些表的内容是否相同(以一种可随意重复的方式)或放弃该项目
答案 0 :(得分:1)
tSQLt可能不是我进行此类测试的首选,它专为单元测试而设计并且完全不受影响 - 对小部分代码进行小规模,通常是快速测试。您可以查看DbFit之类的内容,但也会遇到大量数据。
如果你真的必须证明每个表上每一行的每一列都是相同的,我会考虑在两个数据库中的每个表上创建一个MD5哈希列,然后只查询每个表中哈希不匹配的行。在构建哈希值时,您需要排除在两个表中无法保证的任何列,例如IDENTITY值,加载日期/时间等。例如:
--! Create our existing table structures
create table dbo.ExistingCustomer
(
CustomerId int not null identity(1,1) primary key
, LastName varchar(50) not null
, FirstName varchar(50) not null
, MiddleName varchar(50) null
, TownOfBirth varchar(50) null
, DateOfBirth datetime not null
, NumberOfDependents int not null
, EtlCreatedOn datetime not null
)
go
--! Add a computed column that concatenates all columns of interest
--! into a single string, handling NULL's in the process, then creates
--! 32 characater MD5 hash over the whole string
alter table dbo.ExistingCustomer
add DeltaHash as
convert(nvarchar(32), hashbytes('MD4'
, convert(nvarchar(max)
--! Even though you might reasonably expect first and last names ot be populated,
--! always code defensively has hashing over a series of empty strings will give
--! less confident results
, coalesce(nullif(LastName, ''), 'LastName')
+ coalesce(nullif(FirstName, ''), 'FirstName')
--! This pattern is also effective for nullable columns
+ coalesce(nullif(MiddleName, ''), 'MiddleName')
+ coalesce(convert(char(24), TownOfBirth, 121), 'TownOfBirth')
+ coalesce(cast(NumberOfDependents as varchar(32)), 'NumberOfDependents')) collate Latin1_General_CI_AS), 2) persisted
go
构建此哈希时,您需要非常警惕排序(在两个数据库之间,甚至在列级别)和空格。想象一下,你有一行中有四个整数列,所有这些列都是NULL栏一列。如果用空字符串替换null,则无论第二列还是第三列包含有效整数,MD5哈希都是相同的。
您需要将此列添加到旧表和新加载数据的副本中,然后您可以使用如下查询:
--! 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中运行得非常好,因为所有繁琐的工作都是在服务器端完成的。
您还应该为一个表中存在但不存在于另一个表中的行添加测试。
当然哈希查询不会告诉你差异是什么,但至少可以让你识别出不同的行。
答案 1 :(得分:1)
超时问题证明是在Redgate SQL Test中。因此,长时间运行单元测试的简单解决方案是通过调用tSQLt.Run直接在tSQLt框架中运行它们。在我的测试中,tSQLt似乎没有超时问题。在撰写本文时,我进行了一项单元测试,该测试连续运行了19个小时而没有超时。
这个单元测试花费了很长时间,在我的特定情况下创建了它自己的问题。我将使用散列解决方案或EXCEPT解决方案解决此问题,如@datacentricity响应中的线程所述。