11

假设您有一个网站,它使用一个函数从数据库中检索数据并返回要显示/解析/等的结果......

由于从数据库中检索到的数据是动态的,并且可能在一天中的每一秒都发生变化,那么如何正确地为这个函数编写单元测试呢?

假设该函数应该返回一个结果数组。显然,单元测试可以测试是否返回数组。但是,当由于 MySQL 查询编写错误导致数组本身的内容不正确时会发生什么?数组的大小可能为零,或者数组的内容可能不正确。由于它依赖于不断变化的数据,单元测试如何知道什么是正确的,什么不是?是否有必要从单元测试本身调用数据库,所以有什么可以比较的吗?

您如何为依赖动态数据的函数正确编写单元测试?

4

6 回答 6

10

理想形式的单元测试应该只测试一件事。在这种情况下,您正在测试两件事:

  1. 你的函数的逻辑
  2. 数据库检索

所以我建议进行以下重构:

  1. 将数据库检索逻辑移动到单独的函数中
  2. 让您要测试的功能调用其他功能
  3. 模拟返回数据的函数,以便您可以对应用程序的逻辑进行单元测试
  4. 如果有意义(如果您只是依赖另一个库来执行此操作,那么希望该库已经有测试),为动态检索功能编写一个单元测试,您无法测试细节,但可以测试返回数据的结构和合理性(例如,它已设置所有字段,并且是现在 5 秒内的时间)。

此外,通常最好在测试环境中运行单元测试,您可以完全控制数据库中存储的内容。您不想针对生产数据运行这些。

于 2012-04-23T20:56:29.230 回答
2

如果您的函数除了从数据库中提取数据之外还做了一些有趣的事情,您应该将检索提取到不同的函数中并模拟它,以便您可以测试其余部分。

这仍然让您完成测试数据库访问的任务。您不能真正为此进行单元测试,因为根据定义,它不会访问任何数据库,您可以只测试它是否发送您认为应该发送的 sql 语句,但不能测试 sql 语句是否真的有效。

所以你需要一个数据库

您有多种选择:

1)为此类测试创建一个不会被测试更改的固定数据库。

优点:概念上简单缺点:难以维护。测试变得相互依赖,因为它们依赖于相同的数据。无法测试更新、插入或删除的东西(更不用说 DDL)

2)在测试期间创建一个数据库。现在你有两个问题:为测试设置数据库并用数据填充它。

配置:

1)运行一个数据库服务器,为需要运行测试的每个人(至少 devs + ci-server)提供一个用户/模式/数据库。可以使用 hibernate 之类的东西或用于部署的脚本来创建模式。

效果很好,但会让老式的 DBA 发疯。应用程序不得依赖于模式名称。当应用程序使用多个模式时,您也会遇到问题。这个设置相当慢。它可以帮助放入快速光盘。像 RAM 盘

2)有一个内存数据库。从代码开始很容易,而且速度很快。但在大多数情况下,它的行为与您的生产数据库相同。如果你使用一些试图隐藏差异的东西,这就不那么重要了。我经常在第一个构建阶段使用内存数据库,在第二个阶段使用真实数据库。

加载测试数据

1) 人们告诉我使用 dbunit。我不相信当列或约束发生变化时,它似乎有很多 XML 并且难以维护。

2)我更喜欢普通的应用程序代码。(Java + Hibernate)在我的情况下,但是在生产中将数据写入数据库的代码在许多情况下应该适合为您的测试编写测试数据。它有助于有一个特殊的 API 隐藏满足所有外键和东西的细节:http: //blog.schauderhaft.de/2011/03/13/testing-databases-with-junit-and-hibernate-part -1-one-to-rule-them/

于 2012-04-23T21:15:43.087 回答
1

你不能,真的。您需要有保证的静态数据集来创建可靠的单元测试。也许数据库快照对您有用。

动态数据在其他方面也很有用,例如执行回归测试......

于 2012-04-23T20:55:35.897 回答
1

大多数测试侧重于获取数据等涉及的逻辑路径。不是数据本身的有效性。数据的有效性只有在您的应用程序以某种方式计算或聚合数据或其他情况下才有意义,在这种情况下,您应该能够控制输入并验证结果是否正确。

也就是说,有时您确实希望访问您的应用程序用来验证退货的同一数据库。例如,如果您正在测试一个返回过滤数据集的函数,您的单元测试可以执行相同的查询,然后对每个记录的主键进行逐行比较,并验证您的函数是否返回您期望的同一组数据。

我不知道这是否是您的具体问题,但相反,在单元测试中访问数据库以执行断言并没有错。至少我一直这样做,没有人试图让我被捕:)

于 2012-04-23T20:56:11.007 回答
1

忽略您正在谈论数据库的事实,我认为您可能正在寻找您的单元测试来涵盖所有可能导致收益递减的情况。如果我是你,我会介绍一条标准路径,然后介绍几个边缘情况。事实是你不能务实地测试一切。

这里有一些进一步的阅读

http://37signals.com/svn/posts/3159-testing-like-the-tsa
你的单元测试有多深?
http://johnnosnose.blogspot.co.uk/2012/04/re-over-testing.html
http://martinfowler.com/bliki/TestCoverage.html

查看您的特定数据库相关问题,要测试此功能,您可能需要创建一个接缝来预先填充数据,以便您可以涵盖这些情况。

于 2012-04-23T21:01:27.380 回答
1

我会在测试本身中构建数据。这样,您甚至可以针对不断变化的数据测试复杂的场景。关键是您可以通过拥有专用的测试数据库来控制测试中的数据更改,

第 1 步:将您需要的数据插入仅供测试使用的数据库 第 2 步:数据库现在处于稳定的可预测状态,因此您可以运行查询并测试输出

于 2012-04-24T05:01:26.567 回答