编写数据访问代码测试–不要忘记数据库

在为数据访问代码编写测试时,必须遵循以下三个规则:

  1. 我们的测试必须使用真实的数据库架构。
  2. 我们的测试必须是确定性的。
  3. 我们的测试必须断言正确的事情。

这些规则是显而易见的

这就是为什么有些开发人员破坏了它们(我过去也曾破坏它们)的原因。

这篇博客文章描述了为什么这些规则很重要并帮助我们遵循它们。

规则1:我们必须使用真实的数据库架构

本系列的第二部分告诉我们,应该通过使用与应用程序相同的配置来配置集成测试。 如果我们有充分的理由这样做的话,它也告诉我们可以打破该规则。

让我们研究一种非常普遍的情况,其中我们的集成测试使用与应用程序不同的配置。

我们可以通过以下方法创建数据库:

  • 我们使用Liquibase创建应用程序的数据库。 启动应用程序时,我们使用其Spring集成对数据库进行必要的更改。
  • 我们让Hibernate创建在集成测试中使用的数据库。

我也这样做了,这感觉像是一个完美的解决方案,因为

  • 我可以享受版本数据库的好处。
  • 编写集成测试就像在公园里散步一样,因为我可以相信Hibernate为集成测试创建了一个有效的数据库。

但是,在开始撰写本教程的博客(数据访问代码的写作测试)之后,我意识到这种方法(至少)存在三个问题:

  • 如果数据库是由Hibernate创建的,则我们无法测试我们的迁移脚本是否创建了正常工作的数据库。
  • Hibernate创建的数据库不一定等于我们的迁移脚本创建的数据库。 例如,如果数据库中的表未描述为实体,则Hibernate不会(自然地)创建这些表。
  • 如果要在集成测试套件中运行性能测试,请使用@Index注释配置所需的索引。 如果我们不这样做,则Hibernate不会创建这些索引。 这意味着我们不能相信性能测试的结果。

我们应该关心这些问题吗?

绝对可以

我们必须记住,每项特定于测试的更改都会在我们的测试配置和生产配置之间造成差异。 如果差异太大,我们的测试就毫无价值。

如果我们不针对将应用程序部署到开发/测试/生产环境时使用的同一数据库模式运行集成测试,那么我们将面临以下问题:

  • 我们不一定要针对某些功能编写集成测试,因为我们的数据库缺少必需的表,触发器,约束或索引。 这意味着我们必须在将应用程序部署到生产环境之前手动测试这些功能。 这是浪费时间。
  • 反馈循环比可能要长得多,因为在将应用程序部署到目标环境后,我们注意到了一些问题(例如由错误的迁移脚本引起的问题)。
  • 如果在将应用程序部署到生产环境中时发现问题,则说明问题扑朔迷离,而我们对此予以了覆盖。 我不喜欢被大便覆盖。 你做?

如果我们想避免这些问题并最大程度地利用数据访问测试的好处,那么集成测试必须使用与将应用程序部署到生产环境中时所使用的数据库模式相同的数据库模式。

规则2:我们的测试必须是确定性的

Martin Fowler 指定非确定性检验如下:

当测试有时通过并有时失败时,如果代码,测试或环境没有任何明显变化,则它是不确定的。 这样的测试失败,然后您重新运行它们,它们就通过了。 此类测试的测试失败似乎是随机的。

他还解释了为什么不确定性测试是一个问题

非确定性测试的麻烦在于,当它们变成红色时,您不知道是由于错误还是仅是部分非确定性行为。 通常,在这些测试中,不确定性失败比较普遍,因此当这些测试变成红色时,您最终会耸耸肩。 一旦您开始忽略回归测试失败,那么该测试就没有用了,您最好将其丢弃。

对我们来说很清楚,不确定性测试是有害的,我们应该不惜一切代价避免使用它们。

那么,非确定性数据访问测试的最常见原因是什么?

我的经验告诉我,非确定性数据访问测试背后的最常见原因是在运行每个测试用例之前,无法将数据库初始化为已知状态。

这很可悲,因为这是一个非常容易解决的问题。 实际上,我们可以使用以下选项之一解决它:

  1. 我们可以使用经过测试的存储库的其他方法将信息添加到数据库中。
  2. 我们可以编写一个库,在运行每个测试之前初始化数据库。
  3. 我们可以使用现有的库,例如DbUnitNoSQLUnit

但是, 我们必须小心,因为只有这些选项才有意义

第一种选择是解决此问题的最坏方法。 它用不必要的初始化代码使我们的测试方法变得混乱,并使它们非常脆弱。 例如,如果我们中断了用于将信息保存到数据库的方法,则使用该方法的每个测试都将失败。

第二个选择要好一些。 但是,当我们可以使用经证明有效的现有库时,为什么还要创建一个新库?

我们不应该重新发明轮子。 我们应该使用最简单,最好的方法来解决这个问题。 我们必须使用现有的库。

规则3:我们必须断言正确的事情

当我们为数据访问代码编写测试时,我们可能不得不编写以下测试:

  1. 从数据库中读取信息。
  2. 将信息写入数据库中的信息。

我们必须写什么样的断言?

首先 ,如果编写测试从数据库中读取信息,我们必须遵循以下规则:

  • 如果我们使用的是将数据库中找到的信息映射到对象的框架或库(例如Spring Data),则断言所返回对象的每个属性值都是正确的是没有意义的。 在这种情况下,我们应确保标识返回对象的属性值正确。 这样做的原因是我们只能使用我们信任的框架或库。 如果我们相信我们的数据访问框架或库能够完成其工作,那么断言所有内容都是没有意义的。
  • 如果我们实现了一个将从数据库中找到的信息映射到对象的存储库,则应确保返回的对象的每个属性值都是正确的。 如果我们不这样做,则无法确保我们的存储库正常工作。

其次 ,如果我们编写将信息写入数据库的测试,则不应在测试方法中添加任何断言。

我们必须使用DbUnit或NoSQLUnit之类的工具来确保将正确的信息存储到数据库中。 这种方法有两个好处:

  • 我们可以在正确的水平上写我们的断言。 换句话说,我们可以验证信息是否确实保存到了使用的数据库中。
  • 我们可以避免使用代码弄乱我们的测试方法,这些代码可以从使用的数据库中查找保存的信息,并验证是否找到了正确的信息。

但是,如果我们要确保将信息保存到数据库的方法返回正确的信息怎么办?

好吧,如果我们自己实现了此方法,则必须为此方法编写两个测试:

  1. 我们必须确保将正确的信息存储到数据库中。
  2. 我们必须验证该方法返回正确的信息。

另一方面,如果此方法是由框架或库提供给我们的, 则不应为其编写任何测试

我们必须记住,我们的目标不是编写确保使用的数据访问框架或库正常工作的断言。

我们的目标是编写断言,以确保我们的代码正确运行。

摘要

这篇博客文章教会了我们四件事:

  • 如果我们想最大程度地利用数据访问测试的好处,那么集成测试必须使用与将应用程序部署到生产环境中时所使用的数据库模式相同的数据库模式。
  • 摆脱不确定性测试很容易。 我们所要做的就是通过使用诸如DbUnit或NoSQLUnit之类的库在每个测试用例运行之前将数据库初始化为已知状态。
  • 如果需要验证是否将正确的信息保存到使用的数据库中,则必须使用诸如DbUnit或NoSQLUnit之类的库。
  • 如果要验证是否从使用的数据库中返回了正确的信息,则必须编写断言以确保我们的代码有效。

翻译自: https://www.javacodegeeks.com/2014/08/writing-tests-for-data-access-code-dont-forget-the-database.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值