我已经考虑了很长时间了。

基本问题是:如何对存储过程进行单元测试?

我看到可以设置单元测试了。对于经典意义上的函数而言,这相对容易(我的意思是,它们获得零个或多个参数并返回一个值)。但是,如果我考虑一个看似简单的过程的真实示例,该过程似乎在某个位置插入一行,并在插入之前或之后执行一些触发器,甚至在定义“单元”的边界时也要执行一些触发器。我应该只测试INSERT本身吗?我认为这很简单-价值相对较低。我应该测试整个事件链的结果吗?除了是否要进行单元测试的问题之外,设计合适的测试可能是一项艰巨的工作,途中还会产生许多其他问号。

然后是不断变化的问题数据。如果UPDATE不仅影响几行,那么在测试用例中必须以某种方式包括每个可能受影响的行。 DELETE的其他问题等等。

那么如何对存储过程进行单元测试?在复杂性上是否有完全无法实现的极限?维护需要哪些资源?

EDIT根据AlexKuznetsov的回答,还有一个小问题:或者有一个完全无效的门槛?

#1 楼

我们已经进行了将近五年的时间,并且我们认为显式测试修改肯定是可行的,但速度相当慢。
此外,除非我们使用单独的数据库,否则我们无法轻松地从多个连接中同时运行此类测试。相反,我们应该隐式地测试修饰-我们使用它们至少构建一些测试数据,并验证我们的选择是否返回了预期结果。

我写了一篇题为“关闭那些漏洞”:从单元测试T-SQL中学到的经验教训以及一些博客文章

关于您的问题“复杂性中是否存在完全没有希望的复杂性?”,复杂的模块比简单的模块需要更多的测试。

为了简化维护,我们生成了预期的结果,并将它们存储在单独的文件中-产生了很大的不同。

#2 楼

是的,您应该将整个事件链作为一个单元进行测试。因此,在示例中,该过程插入到表中并导致触发多个触发器,因此您应该编写单元测试,以评估该过程的各种输入。每个单元测试应该通过还是失败,具体取决于它是否返回正确的值,正确更改表的状态,创建正确的电子邮件,甚至发送正确的网络数据包(如果旨在执行此操作)。简而言之,应该验证单元的每种效果。

您是正确的,设计单元测试需要做一些工作,但是大部分工作必须手动完成,而您只是在节省测试单元所需的工作,以便将来进行更改时,测试可以变得更加彻底和容易。

更改数据确实使测试更加困难,但是并没有测试的重要性降低了,实际上增加了单元测试的价值,因为大多数困难只需要经过一次思考,而不是每次对单元进行更改时都要考虑。保存的数据集,作为设置/拆卸的一部分的插入/更新/删除以及范围狭窄的操作都可以用来简化此过程。由于问题不是特定于数据库的,因此细节会有所不同。

高端或低端没有复杂性阈值,因此不应阻止您进行测试或单元测试。请考虑以下问题:


您是否始终编写无错误代码?
小型单元是否始终无错误?
大型单元是否有错误是否可以? ?
需要多少个bug才能造成灾难?

假设您开始一项新工作,并且要对许多地方使用的小功能进行优化。整个应用程序是由一个甚至没有人记得的员工编写和维护的。这些单位有描述正常预期行为的文档,但仅此而已。您更希望找到其中哪一个?


应用程序中的任何地方都没有单元测试。进行更改后,您可以对设备本身进行一些手动测试,以确保它仍然返回文档中的预期值。然后,您可以将其推广到生产环境,指望并希望它能工作(毕竟,您始终编写无错误代码,并且一个单元中的优化永远不会影响另一个单元),或者花费大量时间学习整个应用程序的方式可以使您可以直接或间接手动测试每个单元。
整个应用程序中的单元测试每天或按需自动运行。他们不仅检查正常的输入值及其预期的响应,还检查异常的值和引发的预期异常。您进行更改并立即运行该应用程序的单元测试套件,看到其他三个单元不再返回预期结果。其中两个是良性的,因此您需要调整单元测试来解决这个问题。第三个需要稍作调整和小的新单元测试。进行更改后,整个测试套件成功,您可以放心地进行更改。


评论


首先,感谢您的回答-在这个问题上,我没想到会有任何进一步的进步...其次,我必须承认您对简单的操作是正确的:即使是两列的INSERT也会产生错误。如果编写它的目的是使列顺序可以与参数匹配,那么可以,但是那又是对的:最好将整个应用程序保持在测试状态下。

– dezso
13年2月22日在21:13

@dezso这是一个很好的问题,也是一个需要在数据库世界中获得更多了解的概念。

–雷·里菲尔(Leigh Riffel)
13年2月25日在13:19

“您应该将整个事件链作为一个单元进行测试” –这是您可以说的最违反直觉的事情。如果是这样,它就不是单位。您正在进行集成测试

–乔·菲利普斯(Joe Phillips)
17年9月9日在15:29

@Joe Philips-随心所欲,确保执行插入操作并触发一些触发器的过程符合自动测试的要求。

–雷·里菲尔(Leigh Riffel)
17年11月9日在19:10

#3 楼

对于PostgreSQL,请查看pgTAP:


pgTAP是一组数据库函数,可以很容易地编写pbr脚本或xUnit风格的测试函数中的TAP发射单元测试。 。


评论


看到了,谢谢。有人有经验吗?

– dezso
2012年7月19日在7:16

是的,如今有很多人。如有疑问,请订阅邮件列表。

–理论
2014年1月15日17:13

#4 楼

如果您希望完全在SQL上完成对存储过程的测试,请访问http://tsqlt.org/

,它与MS SQL 2005 SP2及更高版本兼容,并且优点是开发人员无需了解C#或另一种语言即可实现测试。

还有一些工具可以执行模拟表和视图,以帮助您实现可重新运行的测试套件。 br />

#5 楼

实际上,与以预期方式运行存储过程或函数然后检查结果的实际功能测试的价值相比,SQL中的“单元测试”价值不大。通常,这意味着要在接近生产的数据和系统上运行测试。

单元测试可以轻松解决逻辑上的问题。对于非常简单的过程和函数,其中几乎没有逻辑并且不是跨数据库的,因此单元测试很好。黄金标准应该是功能测试。例如,我很难为处理复杂的业务流程测试的工作编写单元测试,例如跨多个数据库同步用户安全性。

归根结底,单元测试创​​建了大量的代码-必须维护的代码-并没有提供与实际测试功能正确的结果相同的功能测试程序或一系列程序。

编写功能测试还创建了必须维护的代码,而且还提供了大多数开发人员和DBA可以理解的代码。无意冒犯,但是t-sqlt使用起来不那么直观,并且在跨数据库过程中存在缺陷-这些缺陷导致生成更多的代码(必须维护的代码)。

更少的代码更好。影响力更大的代码更好。单元测试在开发人员世界中很流行,并且也渗入了SQL开发领域,但是最终,如果将用户添加到数据库的SQL过程中断了,那么为它编写单元测试就很容易了揭示破损。

严格执行流程测试的功能测试是最好的,而处理业务关键数据的作业的集成测试则是必需的。