仅供参考:本文是我关于SQA第25天的报告的扩展版本。根据与同事沟通的经验,我可以说在数据库中测试代码不是一种常见的做法。 这可能是潜在的危险。 数据库中的逻辑由编写“常规”代码的同一个人编写。 因此,那里也可能出现错误,并且还可能给产品,企业和消费者带来负面影响。 不管是帮助后端的存储过程,还是将数据转换为存储的ETL,都存在风险,测试可以大大降低风险。 我想告诉您什么是tSQLt,以及它如何帮助我们在SQL Server中测试代码。
语境
SQL Server上有一个大型仓库,其中包含各种临床研究数据。 它来自各种来源(主要是面向文档的数据库)。 在服务器本身内部,使用ETL反复转换数据。 将来,可以将这些数据上载到较小的数据库,以供解决某些小的特定问题的Web应用程序使用。 一些客户的客户还要求API以满足其内部需求。 在此类API的实现中,经常使用存储过程和查询。
通常,代码按顺序位于DBMS一侧。
为什么需要所有这些
从介绍中已经可以理解,数据库中的代码是相同的代码
应用程序,就像其他应用程序一样,也可能会出现错误。
我认为许多人都知道该bug的价格取决于其发现的时间,该发现通常归因于Barry Bohem。 由于需要反复进行很多阶段(编码,单元,集成,系统测试等)来定位错误并将校正后的代码带回原处,因此在早期阶段发现并在后期阶段发现的错误可能会更加昂贵。发现问题的阶段。 该效果也与仓库情况有关。 如果错误蔓延到某个ETL中,并且数据经历了多次转换,那么如果在数据中检测到错误,则必须:
- 重新执行所有转换步骤以定位问题。
- 解决问题。
- 重新获取更正的数据(不排除手动更正)。
- 验证由错误引起的错误数据没有出现在其他地方。
不要忘记我们不出售毛绒玩具。 临床研究等领域的错误不仅会给企业造成伤害,还会给人们的健康造成伤害。
怎么测试?
因为我们在谈论代码测试,所以我们指的是单元测试和集成测试。 这些事情非常排练,涉及不断的回归。 严格来说,没有人手动进行这样的测试(嗯,也许除了一些完全退化的案例外)。
一个不错的好处:在记录代码时,测试可以作为辅助材料。 顺便说一句,客户需求可能看起来像这样(可点击):
Excel,带有需求的两列+其他列中分散的支持信息+标注标记,这比提供帮助更令人困惑。 如有必要,恢复原始愿望可能很困难。 测试可以帮助更准确地捕获实现的细微差别(当然,您不应将它们视为等效的文档)。
不幸的是,随着代码的复杂性,测试的复杂性也在增加,并且这种影响可以得到平均化。
测试可以作为抵御自发海象的附加安全层。 由于形式主义导致的CI自检有助于解决此问题。
如果我们的选择落在自动化的道路上,那么我们需要确定实现自动化的工具。
怎么测试?
在测试数据库中的代码的情况下,我区分两种方法:基于SQL的(即直接在DBMS中运行)和基于非SQL的方法。 我能够强调以下细微差别:
在SQL Server中,我们有一些选择:
对“好不好”的评价是主观的,抱歉,没有这个,就没有地方。
说明:“首次出现”是我设法找到的框架生命周期中最早的日期,即最早的发布或提交。
您可能会注意到,基于SQL的替代方法已经放弃了相当长的一段时间,并且tSQLt是唯一受支持的选项。 从功能上讲,tSQLt也可以胜出。 唯一的问题是,在断言方面,TST的选择比tSQLt稍微丰富一些,但是,tSQLt的缺点不大。
tSQLt文档中有细微差别,但我稍后会讨论。
在非SQL驱动的世界中,事情并不是那么简单。 尽管不是超级活跃的替代方案正在开发中。 DbFit是一个基于FitNesse框架的有趣工具。 他提供有关Wiki标记的写作测试。 Slacker也是一个奇怪的事情-为数据库编写测试时的BDD方法。
我将讨论非SQL驱动的断言。 从表面上看,它们的数量减少了,因此可以说它们更糟。 但是在这里值得考虑的是它们与tSQLt根本不同。 并非一切都那么简单。
最后一行是“ NUnit等” -这是一个提醒。 日常工作中熟悉的许多单元测试框架都可以使用辅助库在辅助数据库上使用。 该表有很多N / A,因为该行实际上包括许多工具。 断言列中的“细微差别”来自同一点-在不同的工具中,它们的设置可能有所不同,并且对数据库的适用性问题尚待解决。
作为另一个有趣的指标,我们可以考虑
Google的趋势 。
细微差别:
- 我没有包括Slacker,因为这个名称可能意味着很多事情(并且“ Slacker framework”之类的请求在图表上并不是特别明显)。
- 出于好奇,TST趋势有所增加,但是它也并不能反映事务的状态,因为它是一个缩写,表示很多不同的事物。
- 我没有包括NUnit及其类似物,因为它们最初是用于测试应用程序自身代码的框架,并且它们的趋势并不表示我们的情况。
一般而言,可以说tSQLt在类似物的背景下看起来很不错。
什么是tSQLt?
您可能会猜到,tSQLt是一个基于SQL的单元测试框架。
→
官方网站从2005 SP2开始,已经宣布了对SQL Server的支持。 我从来没有能够回顾过去,但是在开发服务器上有2012年,在本地有2017年-没问题。
开源的Apache 2.0许可
可在GitHub上获得 。 您可以在商业项目中免费进行分叉,走私和使用,而且最重要的是,不要害怕CLR中的书签。
工作技工
测试用例是存储过程。 它们被组合成测试类(以xUnit表示的测试套件)。
测试类只不过是常规的数据库模式。 它们在框架表中的注册与其他方案不同。 您可以通过调用一个过程-tSQLt.NewTestClass进行这种注册。
在测试类内部,还可以定义一个SetUp过程,该过程将在每个单独的测试用例执行之前运行。
不需要拆卸测试程序即可在测试用例完成时恢复系统。 每个测试用例与SetUp过程一起在单独的事务中执行,该事务在收集结果后回滚。 这很方便,但是会带来一些负面影响,我将在下面讨论。
该框架允许您一次运行单个测试用例,整个测试类或所有已注册的测试类。
示例功能
我不想重述已经很简单的官方指南,而是使用示例来讨论该框架的可能性。
免责声明:- 例子被简化;
- 最初,并不是所有的测试代码都是由我编写的,而是集体创造力的结晶。
- 发明示例2是为了更全面地演示tSQLt的功能。
示例1:CsvSql
应一位客户的要求,实施了以下方法。 Nvarchar(MAX)字段中的数据库存储SQL查询。 要查看这些查询,需要使用最少的前端。 这些请求返回的结果集被后端用来生成CSV文件,以通过API调用返回。
结果集非常繁重,包含许多列。 这种结果集的条件示例:
该结果集是一些临床试验数据。 让我们仔细看看ClinicsNum的考虑方式-研究中涉及的诊所数量。 我们有两个表:[Trial]和[Clinic]:
有FK:[Clinic]。[TrialID]-> [Trial]。[TrialID]。 显然,要计算诊所数量,我们只需要通常的COUNT(*)。
SELECT COUNT(*), ... FROM dbo.Trial LEFT JOIN dbo.Clinic ON Trial.ID = Clinic.TrialID WHERE Trial.Name = @trialName GROUP BY ...
我们如何测试这样的请求? 首先,我们可以使用方便的存根-FakeTable,它将大大简化后续工作。
EXEC tSQLt.FakeTable 'dbo.Trial'; EXEC tSQLt.FakeTable 'dbo.Clinic';
FakeTable做一件简单的事情-重命名旧表并在其位置创建新表。 相同的名称,相同的列,但没有约束'ov和trigger'ov。
我们为什么需要这个:
- 测试数据库中可能有一些数据可能会干扰测试。 感谢FakeTable,我们不依赖于它们。
- 通常,对于测试,您只需要填写几列。 表格中可能有很多,其中有些会有约束。 这样,我们简化了测试数据的进一步安装-我们将仅插入测试用例真正需要的那些数据。
- 插入测试数据时,我们绝对不会影响任何触发器,也不必担心不必要的后效应。
接下来,插入我们需要的测试数据:
INSERT INTO dbo.Trial ([ID], [Name]) VALUES (1, 'Valerian'); INSERT INTO dbo.Clinic ([ID], [TrialID], [Name]) VALUES (1, 1, 'Clinic1'), (2, 1, 'Clinic2');
我们从数据库中获取查询,创建实际表并用查询的结果集填充它:
DECLARE @sqlStatement NVARCHAR(MAX) = (SELECT… CREATE TABLE actual ([TrialID], ...); INSERT INTO actual EXEC sp_executesql @sqlStatement, ...
填写预期-预期值:
CREATE TABLE expected ( ClinicsNum INT ); INSERT INTO expected SELECT 2
我想提请您注意以下事实:在Expected表中,只有一列,而在Actual中,则有一组。
这是由于AssertEqualsTable过程的功能所致,我们将使用该过程检查值。
EXEC tSQLt.AssertEqualsTable 'expected', 'actual', 'incorrect number of clinics';
它仅比较两个被比较表中存在的列。 在我们的例子中,这很方便,因为测试查询返回很多列,每个列都有自己的逻辑,有时会很混乱。 我们不想增加测试用例,因此此功能对我们非常有用。 当然,这是一把双刃剑。 如果在“实际”中通过“ SELECT TOP 0”自动填充了一组列,并且在某个时刻突然出现了一个额外的列,则此刻的测试用例将无法解决。 要处理这种情况,您需要进行其他检查。
姊妹程序AssertEqualsTable
值得一提的是,tSQLt包含两个类似于AssertEqualsTable的过程。 这些是AssertEqualsTableSchema和AssertResultSetsHaveSameMetaData。 第一个与AssertEqualsTable相同,但是在表元数据上。 第二个进行了类似的比较,但是对结果集的元数据进行了比较。
示例2:约束
在前面的示例中,我们看到了如何删除constraint'y。 但是,如果我们需要检查它们怎么办? 从技术上讲,这也是逻辑的一部分,也可以视为测试覆盖范围的候选对象。
考虑上一个示例的情况。 两个表-[Trial]和[Clinic]; [TrialID] FK:
让我们尝试编写一个测试用例来测试此约束。 首先,与上次一样,我们伪造表。
EXEC tSQLt.FakeTable '[dbo].[Trial]' EXEC tSQLt.FakeTable '[dbo].[Clinic]'
目标是相同的-摆脱不必要的限制。 我们希望没有不必要的手势的孤立检查。
接下来,我们使用ApplyConstraint过程将所需的约束返回到位置:
EXEC tSQLt.ApplyConstraint '[dbo].[Clinic]', 'Trial_FK';
在这里,我们将方便的配置放在一起进行直接验证。 检查本身将包含以下事实:尝试插入数据将不可避免地导致异常。 为了使测试用例正常工作,我们需要捕获此异常。 ExpectException异常处理程序将帮助我们解决此问题。
EXEC tSQLt.ExpectException @ExpectedMessage = 'The INSERT statement conflicted...', @ExpectedSeverity = 16, @ExpectedState = 0;
安装处理程序后,您可以尝试插入不可插入的内容。
INSERT INTO [dbo].[Clinic] ([TrialID]) VALUES (1)
捕获异常。 测试通过。
姊妹程序ApplyConstraint
TSQLt开发人员为我们提供了一种类似的测试触发器的方法。 您可以使用ApplyTrigger过程将触发器返回到伪表。 此外,一切都与上面的示例相同-我们触发触发器,检查结果。
ExpectNoException-ExpectException的反义词
对于绝对不应发生异常的情况,有一个ExpectNoException过程。 它与ExpectException的工作方式相同,不同之处在于,如果发生异常,则认为测试失败。
示例3:信号量
情况如下。 有许多存储过程和Windows服务。 它们执行的开始可能是由各种外部事件引起的。 在这种情况下,其执行的允许顺序是固定的。 信号量用于区分对数据库表的访问。 它是一组存储过程。
例如,考虑以下过程之一。 我们有两个表:
表[Process]包含允许执行的进程的列表,[ProcStatus]-这些进程的状态的列表。
我们的程序做什么? 调用时,首先进行一系列检查:
- 在过程参数中传递的要启动过程的名称在[Process]表的[Name]字段中查找。
- 如果找到了进程名称,则检查当前是否可以启动它-[Process]表的[IsRunable]标志。
- 如果事实证明该过程可以执行,那么仍然可以确保该过程尚未运行。 在表[ProcStatus]中,检查是否存在状态为'InProg'的有关此过程的记录。
如果一切正常,则将有关此过程的状态为“ InProg”的新记录添加到ProcStatus(这被视为启动),该记录的ID与ProcStatusId参数一起返回。 如果任何验证失败,那么我们期望以下几点:
- 一封信将发送给系统管理员。
- 返回ProcStatusId = -1。
- 未在[ProcStatus]中添加新条目。
让我们编写一个测试用例,以测试该过程不在可接受列表中的情况。
为了方便起见,请立即应用FakeTable。 在这里,它并不是那么重要,但它可能会有用:
- 我们保证会删除所有可能干扰测试用例正确执行的数据。
- 我们将简化ProcStatus中缺少条目的进一步验证。
EXEC tSQLt.FakeTable 'dbo.Process'; EXEC tSQLt.FakeTable 'dbo.ProcStatus';
要发送消息,请使用我们的程序员编写的[SendEmail]过程。 要检查发给管理员的信,我们需要接听她的电话。 对于这种情况,tSQLt为我们提供了使用SpyProcedure的功能。
EXEC tSQLt.SpyProcedure 'dbo.SendEmail'
SpyProcedure执行以下操作:
- 创建一个格式为[dbo]。[SendEmail_SpyProcedureLog]的表。
- 与FakeTable一样,它用自己的名称相同但包含日志记录逻辑的原始过程替换。 如果需要,您可以添加任何自己的逻辑。
您可能会猜到,日志记录发生在表[dbo] [SendEmail_SpyProcedureLog]中。 该表包含[_ID_]列-过程调用的序列号。 随后的列带有传递给该过程的参数的名称,并且在调用中传递的值被收集在其中。 如有必要,也可以检查它们。
在调用信号量并进行检查之前,我们需要做的最后一件事是创建一个变量,在该变量中,我们将放置[ProcStatus]表中的记录ID(更确切地说,为-1,因为不会添加记录)。
DECLARE @ProcStatusId BIGINT;
调用信号量:
EXEC dbo.[Semaphore_JobStarter] 'SomeProcess', @ProcStatusId OUTPUT;
就这样,现在我们有了所有必要的数据进行验证。 让我们从检查装运开始。
信件:
IF NOT EXISTS ( SELECT * FROM dbo.SendEmail_SpyProcedureLog) EXEC tSQLt.Fail 'SendEmail has not been run.';
在这种情况下,我们决定不检查发送过程中发送的参数,而只是检查事实本身。 我提请您注意tSQLt.Fail过程。 它允许您“正式”失败测试用例。 如果需要构建一些特定的结构,则tSQLt.Fail将允许您执行此操作。
接下来,检查[dbo]中是否缺少条目[ProcStatus]:
EXEC tSQLt.AssertEmptyTable 'dbo.ProcStatus';
这是我们一开始就应用的FakeTable帮助我们的地方。 多亏他,我们才能期待空虚。 没有它,为了进行准确的验证,我们应该很好地比较信号量前后的记录数量。
等于ProcStatusId = -1,我们可以轻松地使用AssertEquals进行检查:
EXEC tSQLt.AssertEquals -1, @ProcStatusId, 'Wrong ProcStatusId.';
AssertEquals非常简单-它只比较两个值,没有什么超自然的。
AssertEquals同级程序
为了比较这些值,我们提供了许多过程:
- 断言等于
- 断言不等于
- AssertEqualsString
- Assertike
我认为他们的名字说明了一切。 唯一值得注意的是存在单独的AssertEqualsString过程。 问题是AssertEquals / AssertNotEquals / AssertLike与SQL_VARIANT一起使用,并且NVARCHAR(MAX)不适用于它,因此tSQLt开发人员必须分配一个单独的过程来测试NVARCHAR(MAX)。
伪造功能
可以稍作拉伸的FakeFunction可以称为类似于SpyProcedure的过程。 这种伪造品使您可以用必要的简单功能替换任何功能。 由于SQL Server中的功能是按照带有牙膏的管子的原理工作的-它们通过“唯一的技术漏洞”给出结果,因此,不幸的是,没有提供记录功能。 只能替代逻辑。
陷阱
值得注意的是,在使用tSQLt时可能会遇到一些陷阱。 在这种情况下,我的陷阱是指由于SQL Server的限制而产生的某些问题,以及/或者框架的开发人员无法解决的问题。
交易取消/损坏
我们团队面临的第一个也是最重要的问题是取消交易。 SQL Server不知道如何单独回滚嵌套的事务-仅将所有内容作为整体回滚到最外部。 鉴于tSQLt将每个测试用例包装在单独的事务中的事实,这成为一个问题。 毕竟,测试过程中的事务回滚可能会中断测试执行,从而导致非描述性执行错误。
为了避免这个问题,我们使用保存点。 这个想法很简单。 在测试过程中开始事务之前,我们检查是否已经在事务中。 如果结果是肯定的,那么我们假设这是一个tSQLt事务,则将保存点放入而不是开始。 然后,如有必要,我们将回滚到该保存点,而不是事务开始。 这样的嵌套不是。
事务损坏使问题复杂化。 如果突然出了点问题并且异常起作用了,那么交易可能就注定了。 这样的事务不仅可以提交,还可以回滚到保存点,仅回滚整个事情。
鉴于以上所有内容,您必须应用以下设计:
DECLARE @isNestedTransaction BIT = CASE WHEN @@trancount > 0 THEN 'true' ELSE 'false' END; BEGIN TRY IF @isNestedTransaction = 'false' BEGIN TRANSACTION ELSE SAVE TRANSACTION SavepointName;
考虑部分代码。 首先,我们需要确定我们是否在事务内:
DECLARE @isNestedTransaction BIT = CASE WHEN @@trancount > 0 THEN 'true' ELSE 'false' END;
收到@isNestedTransaction标志后,根据情况运行TRY块并设置保存点或事务的开始。
BEGIN TRY IF @isNestedTransaction = 'false' BEGIN TRANSACTION ELSE SAVE TRANSACTION SavepointName;
完成一些有用的操作后,如果这是过程的“真实”开始,请提交。
当然,如果这是从测试用例启动的,则无需提交任何内容。 在执行结束时,tSQLt会简单地回滚所有更改。 如果突然出了点问题,我们进入了CATCH块,那么首先要做的就是找出我们的交易是否可以提交。
BEGIN CATCH DECLARE @isCommitable BIT = CASE WHEN XACT_STATE() = 1 THEN 'true' ELSE 'false' END;
只有在以下情况下,我们才能回滚到保存点:
- 委托交易
- 进行测试运行,即 保存点存在。
在其他情况下,我们需要回滚整个交易。
IF @isCommitable = 'true' AND @isNestedTransaction = 'true' ROLLBACK TRANSACTION SavepointName; ELSE ROLLBACK; THROW; END CATCH;
是的,不幸的是,如果在测试运行期间我们遇到了无法提交的事务,那么在执行测试用例时仍然会出错。
FakeTable和外键问题
考虑熟悉的表[Trial]和[Clinic]:
我们记得[TrialID] FK。 这会导致什么问题? 在前面给出的示例中,我们一次将FakeTable应用于两个表。 如果仅将其应用于[Trial],则会出现以下情况:
尝试以这种方式在[诊所]中插入条目可能会失败(即使我们在[Trial]表的伪造版本中准备了所有必要的数据)。
[dbo].[Test_FK_Problem] failed: (Error) The INSERT statement conflicted with the FOREIGN KEY constraint "Trial_Fk". The conflict occurred in database "HabrDemo", table "dbo.tSQLt_tempobject_ba8f36353f7a44f6a9176a7d1db02493", column 'TrialID'.[16,0]{Test_FK_Problem,14}
结论:您需要伪造一切,或者不伪造任何东西。 在第二种情况下,很明显必须事先准备好底座以进行测试。
系统程序上的SpyProcedure
las,监视系统过程的调用将失败。
[HabrDemo].[test_test] failed: (Error) Cannot use SpyProcedure on sys.sp_help because the procedure does not exist[16,10] {tSQLt.Private_ValidateProcedureCanBeUsedWithSpyProcedure,7}
在信号量示例中,我们跟踪了开发人员编写的对[SendEmail]过程的调用。 在这种情况下,编写单独的过程是由于需要在直接发送消息之前收集和处理一些其他信息。 通常,必须为以下事实做好心理准备:一个人可能仅出于测试目的就必须为某些系统过程编写中间层过程。
优势优势
快速安装
安装分为两个阶段,大约需要2分钟。 您只需激活服务器上的CLR(如果尚未完成),然后执行一个脚本即可。 一切都可以添加第一个测试类并编写测试用例。
快速发展
tSQLt是一个易于学习的工具。 我花了几天的时间来掌握它。 我问了使用该框架的同事,结果发现每个人都会花费大约一天的时间。
在CI中快速实施
在我们的项目中,在CI中设置集成大约需要2个小时。 时间当然可以变化,但是通常这不是问题,并且集成可以非常快速地进行。
种类繁多的工具
这是一个主观评估,但是我认为tSQLt提供的功能非常丰富,可以满足实践中绝大部分需求。 在极少数情况下,如果没有足够的内置伪造和断言,则存在tSQLt.Fail。
方便的文档
官方文档既方便又一致。 有了它的帮助,即使这是您的第一个单元测试工具,您也可以在短时间内轻松理解使用tSQLt的本质。
方便的输出
可以以非常清晰的文本形式获取数据:
[tSQLtDemo].[test_error_messages] failed: (Failure) Expected an error to be raised. [tSQLtDemo].[test_tables_comparison] failed: (Failure) useful and descriptive error message Unexpected/missing resultset rows! |_m_|Column1|Column2| +---+-------+-------+ |< |2 |Value2 | |= |1 |Value1 | |= |3 |Value3 | |> |2 |Value3 | +----------------------+ |Test Execution Summary| +----------------------+ |No|Test Case Name |Dur(ms)|Result | +--+------------------------------------+-------+-------+ |1 |[tSQLtDemo].[test_constraint] | 83|Success| |2 |[tSQLtDemo].[test_trial_view] | 83|Success| |3 |[tSQLtDemo].[test_error_messages] | 127|Failure| |4 |[tSQLtDemo].[test_tables_comparison]| 147|Failure| ----------------------------------------------------------------------------- Msg 50000, Level 16, State 10, Line 1 Test Case Summary: 4 test case(s) executed, 2 succeeded, 2 failed, 0 errored. -----------------------------------------------------------------------------
您还可以从数据库中提取(可单击)...
...或以XML格式获取。
<?xml version="1.0" encoding="UTF-8"?> <testsuites> <testsuite id="1" name="tSQLtDemo" tests="3" errors="0" failures="1" timestamp="2019-06-22T16:46:06" time="0.433" hostname="BLAHBLAHBLAH\SQL2017" package="tSQLt"> <properties /> <testcase classname="tSQLtDemo" name="test_constraint" time="0.097" /> <testcase classname="tSQLtDemo" name="test_error_messages" time="0.153"> <failure message="Expected an error to be raised." type="tSQLt.Fail" /> </testcase> <testcase classname="tSQLtDemo" name="test_trial_view" time="0.156" /> <system-out /> <system-err /> </testsuite> </testsuites>
后一个选项使您可以轻松地将测试集成到CI中。 尤其是,在Atlassian Bamboo下,一切对我们都有效。
Redgate支持
这些优点包括对像RedGate这样的大型DBA工具提供程序的支持。 SQL Test-他们的SQL Server Management Studio插件-可以立即使用tSQLt。 此外,正如Google Group的开发人员本人所言,RedGate为使用开发环境的tSQLt主要开发人员提供了帮助。
缺点
没有临时餐桌假货
tSQLt不允许伪造的临时表。 如有必要,您可以使用
非官方插件 。 不幸的是,此加载项仅受SQL Server 2016+支持。
无法访问外部数据库
仅为存储框架保留单独的基础将不起作用。 tSQLt旨在测试同一数据库中的内容。 ake ,,不会起作用。
CREATE PROCEDURE [tSQLtDemo].[test_outer_db] AS BEGIN SELECT TOP 10 * FROM [AdventureWorks2017].[Person].[Password] EXEC tSQLt.FakeTable '[AdventureWorks2017].[Person].[Password]' SELECT TOP 10 * FROM [AdventureWorks2017].[Person].[Password] END
断言似乎起作用,但是没有人保证其性能。
CREATE PROCEDURE [tSQLtDemo].[test_outer_db_assertions] AS BEGIN SELECT TOP 1 * INTO
文档错误
尽管我在上面写过关于文档的一致性和可访问性的事实,但它也包含一些问题。 其中有一些过时的时刻。
示例1.
“快速入门指南”建议从SourceForge下载该框架。 他们
在2015年与SourceForge道别。
示例2.示例中的
ApplyConstraint指南使用带有Fail过程的繁重结构来捕获异常,用ExpectException替换异常会更容易且更直观。
CREATE PROCEDURE ConstraintTests.[test ReferencingTable_ReferencedTable_FK prevents insert of orphaned rows] AS BEGIN EXEC tSQLt.FakeTable 'dbo.ReferencedTable'; EXEC tSQLt.FakeTable 'dbo.ReferencingTable'; EXEC tSQLt.ApplyConstraint 'dbo.ReferencingTable','ReferencingTable_ReferencedTable_FK'; DECLARE @ErrorMessage NVARCHAR(MAX); SET @ErrorMessage = ''; BEGIN TRY INSERT INTO dbo.ReferencingTable ( id, ReferencedTableId ) VALUES ( 1, 11 ) ; END TRY BEGIN CATCH SET @ErrorMessage = ERROR_MESSAGE(); END CATCH IF @ErrorMessage NOT LIKE '%ReferencingTable_ReferencedTable_FK%' BEGIN EXEC tSQLt.Fail 'Expected error message containing ''ReferencingTable_ReferencedTable_FK'' but got: ''',@ErrorMessage,'''!'; END END GO
这是自然的,因为它发生了...
部分遗弃
从2016年初到2019年6月,tSQLt的开发有很长的时间。是的,不幸的是,该工具已被部分弃用。 在
GitHub看来,2019年一点一点,开发仍在向前发展。 尽管正式的Google Groups
有一个线程可以直接向tSQLt的主要开发人员Sebastian询问开发的命运。 最后一个问题是在2019年3月2日提出的,尚未收到答案。
SQL Server 2017问题
如果您使用的是SQL Server 2017,那么对您来说,可能需要安装tSQLt进行一些其他操作。 , 2012 SQL Server security-. «CLR strict security», ( SAFE). (, ,
). .
, , « », tSQLt, , .
GitHub issue , , 2017 (. ).
(±)
. tSQLt . , (CLR, T-SQL SQL ), , . , tSQLt , SQL-powered .
, PostgreSQL
ptTAP . «» PL/pgSQL TAP. MySQL , —
MyTAP . Oracle,
utPLSQL — ( , ) .
结论
, .
— . SQL Server, Oracle MySQL — . , . , , , , , .
— . , , SQL Server, tSQLt 100% , , . , , .