6

我最近参与了一些使用 PHPUnit 的 TDD。我必须测试一个数据库驱动的应用程序,并阅读我计划在未来几周内研究和实施的 DbUnit 扩展。

然而,我遇到了这个人自己的演讲——塞巴斯蒂安·伯格曼——他有一张题为“如果可以的话,避免对 MySQL 进行测试”的幻灯片,这让我对我的冒险行为产生了一些怀疑。

有人可以解释为什么我不应该针对 MySQL 进行测试的原因吗?

谢谢

4

3 回答 3

6

两个原因:

  • 它很慢(并且单元测试需要很快)
  • 它增加了您无法控制的额外故障点(当数据库连接失败时测试真的失败了吗?)

相反,正如作者建议的那样,您应该使用内存数据库(如 SQLite)测试您的 DAL。这消除了上述问题。

但是,这种方法也有其缺点 - 您可能在将 SQL 从一种数据库方言移植到 SQLite 时遇到问题。这自然意味着您将无法测试 DAL 的 MySQL 特定部分。一如既往,它是一把双刃剑——你获得了单元测试的速度和隔离,但你失去了可信度(如果我们可以这样称呼它)——如果它通过 SQLite,你能 100% 确定它在 MySQL 上工作吗?

将 DAL/DAO 测试的核心留给集成测试阶段可能不是一个坏主意,在那里您将使用您使用的真实数据库引擎对其进行测试,并将小东西留给单元测试;例如映射(如果您使用的是 ORM)。

编辑禁食绝不是严格的要求——它只是一个很好的一般建议。在进行 TDD 时,您的开发人员会运行很多单元测试(这样想;每次提交到本地 repo/每个重要的代码更改都需要通过运行单元测试来检查代码库完整性)——也许不是全部,但肯定有一些。你希望这个过程很快。

现在,进行缓慢的测试通常会这样结束:

  • “伙计,这东西运行得太慢了……”
  • “也许我可以只跑几个……我稍后再跑休息”
  • 在这一点上我们知道以后永远不会来
  • “嘿,我最后一次提交没有破坏任何东西,我根本没有运行任何测试!”
  • “我为什么要运行它们呢?”

编写未运行的测试几乎会扼杀编写它们的目的。

这件事发生在一个和我一起工作的朋友身上;他的团队有运行 +/- 20 分钟的测试套件(DAL 测试做得很差,IoC 容器参与测试),开发人员开始运行一些测试,很快“当前构建破坏者”电子邮件成为每天的事情。他们有相当大的套房,但打破测试并没有那么糟糕

总体而言,您的方法似乎是正确的——我不会将测试转移到 SQLite。就像我建议的那样,使用集成测试套件对数据库层进行测试(这样它就可以与常规的单元测试套件分开运行)。

于 2012-07-16T20:38:55.310 回答
3

我完全不同意 Sebastian Bergmann 写的幻灯片 33 的标题。

(因为幻灯片上的信息很容易被误解,我相信他在他的演讲中解释了真正的意思)

正如他在介绍中所说,调试很糟糕,但测试很糟糕

如果实际环境使用 mySQL,则必须针对 mySQL 对其进行测试。否则你会发现自己在调试 SQLite 和 mySQL 之间的区别。

我相信他的方向是针对开发人员而不是单元测试人员,从这个意义上说,我还建议开发人员尽一切努力保持他们的代码数据库尽可能中立。它将提高项目整个生命周期的质量,而不仅仅是单元测试人员。它甚至可以保护 mySQL 本身的功能改进。(过去有很多问题)。

但是,在测试使用 SQL 的单段代码时,无论您已经进行了哪些其他测试,都必须针对真实事物进行测试。

我的做法是创建一个数据库连接的抽象层,并让测试与该层做出反应。随后,我为要测试的每个数据库服务器创建不同的具体类。

在给定的环境下,我们可以针对 SQLite 测试频率,但真正的测试每天至少运行一次。

于 2012-08-04T09:01:02.690 回答
2

我使用 DbUnit 来测试我的模型(说到 MVC,包括 ORM 模型),因为它们与数据库密切相关。对于其他一切(主要是控制器),我使用模拟对象。

单元测试的基本思想是一次只测试一个应用程序代码单元(即一个类),因此最好避免使用 DB,除非代码直接使用它。

当然,性能很重要。例如,我有大约 500 个模型测试,几乎所有模型都使用 DB 和固定装置。在一台速度相当快的计算机上执行所有这些测试大约需要 30-40 秒。代码覆盖率报告生成大约需要一分钟。

于 2012-07-16T20:53:41.320 回答