为啥我应该避免使用 DbUnit 来测试 MySQL?

Posted

技术标签:

【中文标题】为啥我应该避免使用 DbUnit 来测试 MySQL?【英文标题】:Why should I avoid using DbUnit to test MySQL?为什么我应该避免使用 DbUnit 来测试 MySQL? 【发布时间】:2012-07-15 16:21:19 【问题描述】:

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

但是,我遇到了这个人本人的presentation - Sebastian Bergmann - 他有一张幻灯片,标题为“如果可以的话,请避免对 mysql 进行测试”,这让我对我的冒险行为产生了一些怀疑。

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

谢谢

【问题讨论】:

链接的演示文稿不再可用。 :( @SDC:演示文稿 PDF 在non-styled link right below the slideshare box 中提供。 (咆哮:这就是为什么我说a link should look like a link) 【参考方案1】:

两个原因:

速度很慢(单元测试需要很快) 它添加了超出您控制范围的额外故障点(当数据库连接失败时,测试真的失败吗?)

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

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

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

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

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

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

编写不运行的测试,几乎扼杀了编写它们的目的。

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

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

【讨论】:

感谢您的明确答复。我们遇到的问题是我们试图尽可能多地进行单元测试(例如,每个新的错误修复/增强都应该有一个与之关联的单元测试)。当错误修复/增强只是一个新的和改进的 SQL 查询时,这是否合理可行?如果不建议将 DbUnit 用于 Mysql,那么测试这些修复(不使用 SQLite 方法)的标准过程是什么?将所有内容都移植到 SQLite 似乎有点矫枉过正。 另外,为什么单元测试需要快速?我们目前没有优化单元测试过程的要求,我不明白为什么这至关重要? (我是 TDD 的菜鸟,因此不胜感激) 当你经常运行它们时。每次提交时,都应该尽可能多地运行自动化测试。理想情况下,很多(在所有自动化测试运行并通过之前,“构建”不是构建)。如果这需要不到 10 分钟,那么它会令人恼火,但可以接受,但超过 10 分钟就不再可以接受了。 @user1027562:看我的编辑,基本上 mlk 说的是正确的;你运行测试很多,执行时间不应该阻止开发人员运行它们。【参考方案2】:

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

(因为幻灯片上的信息很容易被误解,我是 确保他在演讲中解释了真正的含义)

正如他的介绍中所述调试很糟糕,但测试很困难

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

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

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

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

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

【讨论】:

【参考方案3】:

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

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

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

【讨论】:

以上是关于为啥我应该避免使用 DbUnit 来测试 MySQL?的主要内容,如果未能解决你的问题,请参考以下文章

DbUnit 和二进制数据

执行单元测试时如何防止dbunit生成database.script文件

DBUnit 在每个方法之后都没有清理和插入数据库,所以测试不是独立的

如何在不使用静态 @BeforeClass 的情况下设置/拆卸 dbunit 数据集

Spring Test Dbunit,H2数据库

如何使用 dbUnit 将数据库恢复到初始状态?