你用于测试数据库查询的最佳实践是什么?

13

我目前正在测试我们的解决方案,其中包含了完整的“层次结构”:UI、中间层和无处不在的数据库。

在加入我的当前团队之前,查询测试是由测试人员手动创建查询来理论上返回存储过程应该返回的结果集,基于各种相关性规则、排序等等。

这样做的副作用是错误经常被归咎于测试人员的查询而不是实际的问题查询。

我建议使用已知的结果集进行测试,因为你可以控制存在的数据,从而推断出它应该如何返回。以前,数据是从生产环境中提取的,经过清理后才填充到我们的测试数据库中。

人们仍然坚持创建自己的查询来测试开发人员所创建的内容。我怀疑许多人仍然这样做。我认为这根本不是理想情况,并且会不必要地增加我们的测试范围。

那么,我很好奇您用什么方法来测试这种场景,什么被认为是最理想的端到端测试覆盖方式,而不会引入混沌数据?

我遇到的问题是最佳测试位置在哪里。我应该直接测试服务,并将数据集与我可以从存储过程中获取的数据集进行比较吗?我有一个大致的想法,并且到目前为止已经取得了不错的成果,但是我觉得我们仍然缺少一些重要的东西,所以我正在寻找社区是否有任何有价值的见解,可以帮助更好地制定我的测试方法。


我将继续添加注释,目标环境实际上是所有基于 MS 技术的(SQL、IIS、.NET),好或者坏都在。但是,尽管我缺乏相关技能,我仍然感谢提到使用 Python 等工具的提及。 - Steven Raybell
9个回答

3
测试存储过程需要每个测试人员都有一个单独的数据库实例,这是必需的。如果您共享环境,则无法依赖于测试结果。它们将毫无价值。
您还需要确保在每次测试后将数据库回滚到其先前的状态,以使结果可预测和稳定。由于需要在每次测试后回滚状态,因此这些测试所需的时间比标准单元测试要长得多,因此它们可能是您想要在夜间运行的内容。
有一些工具可用于帮助您完成此操作。DbUnit 是其中之一,我也相信 Microsoft 有一个名为“Visual Studio for Database Professionals”的工具,其中包含了一些支持 DB 测试的功能。

3
以下是一些指导原则:
  1. 在单元测试中使用隔离的数据库(例如,没有其他测试运行或活动)
  2. 始终在同一个测试中插入您打算查询的所有测试数据
  3. 编写测试以随机创建不同数量的数据,例如随机插入1到10行
  4. 随机化数据,例如对于布尔字段,随机插入true或false
  5. 在测试中保留变量的计数(例如,行数,true的数量)
  6. 对于Asserts执行查询并与本地测试变量进行比较
  7. 使用企业服务事务将数据库回滚到先前的状态
有关企业服务事务技术,请参见下面的链接:http://weblogs.asp.net/rosherove/articles/DbUnitTesting.aspx

1
测试1-10行并不能测试太多,尤其是在处理1千万到10亿行数据时,更无法测试性能。 - Jonathan Leffler

1
作为我们持续集成的一部分,我们每晚运行数据库查询的“构建”。这包括一套DB调用,这些调用定期从代码中的实际调用以及任何预期的特别查询进行更新。
这些调用被计时以确保:
1/ 它们不会花费太长时间。
2/ 它们不会与前一晚大相径庭(在不好的方面)。
通过这种方式,我们可以快速捕捉错误的查询或数据库更改。

这些“DB调用套件”,它们是真实调用的克隆吗?如果是,您是否会与代码中的任何查询一起维护它们? - Steven Raybell
我们从代码中提取每个真实调用,并进行一些正则表达式操作,以生成静态查询,然后将此静态调用添加到测试套件中。我们还存储源行,以便检测更改并重新执行它。 - paxdiablo

1
查询规划器是您的好朋友,特别是在这种情况下。检查索引是否被使用以及查询是否需要额外的工作总是一个好习惯。即使您的测试套件中包含了压力测试,捕获昂贵的查询也是一个好主意,以免您的应用程序开始变得缓慢。

1

我们为每个开发人员和测试人员准备了一个空白数据库。

当运行测试时,每个测试都会清除数据库并加载它期望使用的数据。这使我们始终拥有已知状态。

然后,我们可以在同一数据库上测试几种不同的场景(一个接一个),而且我们永远不会影响其他测试人员。

这涵盖了测试数据访问本身。对于服务测试,我们做的事情基本相同,但我们仅测试服务内部 - 我们实际上没有调用服务,而是创建服务处理类的实例并传入我们需要的所有内容。这样,我们就可以测试代码而不是基础设施(消息等)。


1
Django提供了数据库单元测试功能。您可以借鉴他们的设计思路,并在其他环境中复制它。
在Django(和Python)的情况下,最容易从JSON数据提取中填充数据库。其他文件格式可用于其他框架。例如,如果您在Oracle中工作,可能会发现CSV文件更易于使用。
这个TestCase子类允许编写一个看起来很典型的TestCase,用已知的数据夹具来练习数据库。
此外,Django测试运行程序为测试目的创建临时模式。这对Django来说很容易,因为他们有一个完整的对象关系管理组件,包括DDL创建。如果您没有此选项,则仍需要DDL脚本,以便为unittest目的创建和处理测试模式。

1

SQLServerCentral有一篇文章在这里(您可能需要注册,但是它是免费的且没有任何限制),介绍了一个名为tsqlUnit的TSQL单元测试框架。它是开源的,并遵循xUnit框架的传统。

它遵循SEAT TDD模式:

设置 - 通过操作对象、表和/或数据来准备测试条件

练习 - 调用生产代码

断言 - 检查实际结果是否等于预期结果

拆卸 - 将所有内容恢复到测试开始之前的状态。这实际上是通过回滚事务来完成的,这使一切都保持整洁。

虽然我没有使用过它,但它看起来很有前途,我肯定会更详细地研究它。

该框架可以从这里下载。


0

这是一个繁琐的设置,但我建议使用TDD容器。

在运行测试脚本时,构建一个新的容器,其中包含您的数据库运行,用模拟数据进行填充,然后运行查询并针对返回的结果以及查询是否成功进行测试。

这样,您就可以控制测试环境与生产环境的接近程度。

ThoughtWorks


0

我发现测试发送到数据库的SQL语句而不是查询数据库的结果非常有用。

并不是说我不会做后者,但我发现测试SQL语句比让数据库承担太多负担要快得多。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接