63

我正在考虑制作一个新的、轻量级的数据库填充框架。我绝对讨厌 dbunit。在我这样做之前,我想知道是否有人已经这样做了。

我不喜欢 dbunit 的地方:

1) 不推荐使用最简单的编写和入门格式。他们希望您使用臃肿的格式。有些甚至需要 xml 模式。好吧,无所谓了。

2)它们不是按照您编写它们的顺序填充行,而是按照在 xml 文件中定义的顺序来填充行。这真的很糟糕,因为您不能以外键约束不会导致问题的方式对数据进行排序。这只会迫使您经历完全关闭它们的麻烦。

这也会浪费时间并使您的 junit 基类膨胀以包含禁用外键约束的代码。您可能必须测试数据库类型(hsqldb 等)并以特定于数据库的方式禁用它们。这太糟糕了。

如果 dbunit 帮助自动禁用外键约束作为其框架的一部分,可能会更好,但他们不这样做。他们确实跟踪方言......那么为什么不使用它们呢?最终,所有这些都会迫使程序员浪费时间而不是快速起床并进行测试。

3) XML 写起来很痛苦。关于这一点我不需要多说。他们还提供了很多方法来做到这一点,我认为这只会使事情复杂化。只需提供一种真正可靠的方法并完成它。

4)当您的数据变大时,跟踪 id 及其一致/正确的关系是一件非常痛苦的事情。

另外,如果你一个月不做一个项目,你怎么记住 user_id 1 是管理员,user_id 2 是业务用户,user_id 3 是工程师,user_id 4 是别的什么?回去检查这会浪费更多时间。除了任意数字之外,应该有一种有意义的方式来检索它。

5)它很慢。我发现除非使用 hsqldb,否则它会非常缓慢。它不一定是。还有很多方法可以弄乱它的配置,因为“开箱即用”并不容易。您必须经历一个驼峰才能使其正常工作。所有这些都是鼓励人们不要使用它,或者当他们开始使用它时会生气。

6)有些值往往会重复很多,比如日期。最好指定默认值,甚至让框架自动放入默认值,即使您没有告诉它在其中放入默认值。这样你就可以只用你想要的值来创建对象,剩下的就不用了。如果不需要,这肯定优于指定列的每个角落和缝隙。

7)可能最烦人的事情是第一个条目必须包含所有值 - 甚至是空占位符 - 否则未来的行将不会选择您实际指定的列。

DBunit 也没有将 [NULL] 转换为真正的空值的合理默认值。您必须手动添加它。告诉我,谁没有用 dbunit 做过这个?每个人都有。不应该是这样的!

这意味着,如果您有一个多态对象,则必须在第一行中为每个子类的连接表声明所有外键,即使它们为空。如果您为所有子类模式创建一个表,您仍然必须在第一行指定所有字段。这太可怕了。

有什么可以让我满意,还是我应该成为更好的数据库测试框架的下一个框架开发人员?

4

14 回答 14

102

我不知道有任何真正的 DbUnit 替代品,@Joe 提到的工具都没有出现我的眼中:

  • Incanto:不是 DB 不可知论者
  • SQLUnit:用于测试数据库存储过程的回归和单元测试工具(这不是 DbUnit 的意义所在)
  • Cactus:容器内测试工具(我看不出它对数据库有什么帮助)
  • Liquibase:数据库迁移工具(不加载/验证数据)
  • ORMUnit:可以初始化数据库,仅此而已
  • JMock:根本不与 DbUnit 竞争

话虽如此,我个人已经在小型和大型项目中多次成功使用 DbUnit,我发现它非常有用,尤其是在使用Unitils及其 DbUnit 模块时。这并不意味着它是完美的并且无法改进,而是使用不错的工具(定制或类似 Unitils 的工具),使用它是一种不错的体验。

所以让我回答你的一些观点:

  1. 不推荐使用最简单的编写和入门格式。他们希望您使用臃肿的格式。有些甚至需要 xml 模式。好吧,无所谓了。

DbUnit 支持平面或结构化 XML、XLS、CSV。您想使用哪种革命性格式?顺便说一句,使用 XML 时,DTD 或模式不是必需的。但是它给了你很好的东西,比如验证和自动完成,那有什么不好?而且 Unitils 可以为您轻松生成它,请参阅Generate an XSD or DTD of the database structure

如果 dbunit 帮助自动禁用外键约束作为其框架的一部分,可能会更好,但他们不这样做。他们确实跟踪方言......那么为什么不使用它们呢?最终,所有这些都会迫使程序员浪费时间而不是快速起床并进行测试。

他们正在等待你的补丁。

同时,Unitils 支持透明地处理约束,请参阅禁用约束和更新序列

  1. XML 写起来很痛苦。关于这一点我不需要多说。他们还提供了很多方法来做到这一点,我认为这只会使事情复杂化。只需提供一种真正可靠的方法并完成它。

我猜疼痛是主观的,但我不觉得它很痛苦,尤其是在使用模式和自动完成时。你建议的银弹是什么?

  1. 当您的数据变大时,跟踪 id 及其一致/正确的关系是一件非常痛苦的事情。

保持它们小,这是一个已知的最佳实践。你违背了一个已知的最佳实践,然后抱怨......

另外,如果你一个月不做一个项目,你怎么记住 user_id 1 是管理员,user_id 2 是业务用户,user_id 3 是工程师,user_id 4 是别的什么?回去检查这会浪费更多时间。除了任意数字之外,应该有一种有意义的方式来检索它。

是的,任务切换会适得其反。但是由于您使用的是低级别数据,因此您必须知道它们是如何表示的,除非您当然使用更高级别的 API(但这不是 DbUnit 的目的),否则没有神奇的解决方案。

  1. 它很慢。我发现除非使用 hsqldb,否则它会非常缓慢。它不一定是。还有很多方法可以弄乱它的配置,因为“开箱即用”并不容易。您必须经历一个驼峰才能使其正常工作。所有这些都是鼓励人们不要使用它,或者当他们开始使用它时会生气。

这是数据库和 JDBC 所固有的,而不是 DbUnit。如果您希望事情尽可能快,请使用像 H2 这样的快速数据库(如果您有更好的不可知论方法来做事,我很乐意了解它)。

  1. 可能最烦人的事情是第一个条目必须包含所有值 - 甚至是空占位符 - 否则未来的行将不会选择您实际指定的列。

当使用 Unitils - Home - JavaPolis 2008Unitils & dbmaintain等演示文稿中提到的 Unitils 时,则不然。

有什么可以让我满意,还是我应该成为更好的数据库测试框架的下一个框架开发人员?

如果你认为你可以让事情变得更好,也许可以为现有的解决方案做出贡献。如果那是不可能的,并且如果你认为你可以创建杀手级数据库测试框架,我能说什么,去做吧。但是不要忘记,咆哮很容易,使用自己的解决方案提出解决方案就不那么容易了。

于 2010-10-16T19:18:29.117 回答
29

作为一名 DbUnit 开发人员,我很感激批评,我必须部分同意你的看法。我们目前正在开始设计下一个 DbUnit 主要版本,我希望邀请您参与讨论和开发。

我不会回答你的观点,因为你的问题与 DbUnit 并没有真正的关系,而是与 DbUnit 的替代品有关。无论如何,我只想强调您的第 7 点是完全错误的:您不再需要指定第一行的所有列,该功能称为列感应。我不会告诉你为什么默认情况下没有启用它,因为你肯定足够聪明,可以自己理解它。

我会给scaladbtest一个深入的检查,希望我们能整合他们的想法。

于 2010-11-03T22:18:59.327 回答
16

面对使用 DBUnit 的类似问题,我发现:http ://dbsetup.ninja-squad.com/index.html可能会解决问题。例如,所有 DB 内容都包含在 java 类本身中,而不是在单独的文件中表示测试数据。

于 2013-04-08T15:56:30.487 回答
4

如果您使用 Spring Framework(或者至少不介意将其用于测试),那么Spring DBUnit是目前我所知道和使用的普通 DBUnit 的最佳(维护)替代品。引用他们的网站:

Spring DBUnit 提供了 Spring 测试框架和流行的 DBUnit 项目之间的集成。它允许您使用简单的注释设置和拆卸数据库表,以及在测试完成后检查预期的表内容。

Spring DBUnit 似乎是用于 DB 单元测试的“有点官方”的 Spring 解决方案(使用 DBUnit);至少图书馆的作者/维护者 Phil Webb 正在 SpringSource/Pivotal 工作。

于 2014-11-18T10:32:29.263 回答
3

我使用 DBUnit,用一些包装器来平滑粗糙的边缘。Jailer是一个可以补充或重叠功能的好工具。它可以从参考数据库中提取数据子集,并将其存储为与 DBUnit 兼容的 XML 文件或“拓扑排序的 DML 文件”,这些文件尊重外键约束。

于 2010-10-16T22:09:25.870 回答
3

我刚刚发布了一个名为 JDBDT(Java 数据库增量测试)的库,您可以将其用于软件测试中的数据库设置和验证。

看看http://jdbdt.org

最佳,爱德华多

于 2016-08-12T17:46:13.333 回答
2

你说得很好。

在过去的几年里,我一直在为许多门户网站工作,主要是使用 PHP,但偶尔也会使用一些 Java。
和你一样,我不明白这些年来框架和单元测试开发人员似乎没有意识到在过去十年中存储处理发生了多少变化。仅仅将创建/插入/截断语句发送到某个数据库是不够的!如果您在大规模运营,您最终会使用各种存储后端,分层组织以快速推出热门内容。另外,在数据库方面还有数据分区的问题。如果您没有提供适当的外键抽象,那么当您的存储设置发生更改时,您肯定会发疯。当我们这样做时:DBUnit.

无论如何,关键是仅仅为单元测试准备一个基本的数据库存储对于复杂的存储设置是不够的,因为它们经常无法在实时环境中重现问题并且维护起来很麻烦。

不想听起来像个狂热分子:事情还可以的一个地方是ruby on rails。这有一个持久的模型概念,人们似乎实际上已经投入了一些思考。如果你在做生意PHPSymfony那就是去的地方。它受到默认包含的限制Doctrine,其中也非常以 DB 为中心,但它具有干净的接口和出色的可扩展性,并且完全复制了 rails 夹具系统。从专业上讲,我现在需要坚持使用自制解决方案,但它们工作正常。

于 2011-12-14T20:44:17.067 回答
2

另一个投票赞成用现代库包装 DBUnit 以提高可用性和简洁性。我的选择是database-rider,它使 DBUnit 易于使用,甚至支持 JUnit 5,如下例所示:

@RunWith(JUnitPlatform.class)
@ExtendWith(DBUnitExtension.class)
@DBUnit(cacheConnection = true, cacheTableNames = true)
class TestInstrumentQueryService {

    private ConnectionHolder connHolder = () -> EntityManagerProvider.instance("my-jta-unit").connection();

    @DBRider
    @DataSet("datasets/instrumentIds.yml")
    void testFindInstrumentById() {

        InstrumentQueryService iqs = new InstrumentQueryService(EntityManagerProvider.em());

        Instrument instr = iqs.findInstrumentById(InstrumentIdType.TICKER_BBG, "AAPL");
        assertEquals(100, instr.getId());
    }
}

请注意这如何允许无缝地利用(简洁的)YAML 测试数据集(YAML 不是 XML,尽管我相信 DBUnit 实际上原生支持这些数据集)。

于 2020-02-14T17:52:33.933 回答
1

下面是一些我特别喜欢或觉得有趣的工具(除了 DBunit)的简短列表。至少他们可能会提供一些灵感:

请注意,就范围或功能集而言,这些都不是 DBunit 的真正竞争对手。但是,那里有一些有趣的想法可能值得一看。祝你好运!

于 2010-10-16T17:58:46.833 回答
1

我们正在编写Daleq作为 DbUnit 的包装器,以解决上面提到的一些问题。它允许仅在单元测试中填充数据库,而不是依赖于编辑 XML 文件。

于 2012-08-31T18:52:14.903 回答
1

DBUnit 也有类似的问题。特别是使用它来填充本地开发数据和从真实数据库中导出数据。我遇到了几种情况,它会导出一个无法导入的数据集。

这激发了我为它编写一个新库:https ://github.com/jeffskj/phonydata

这使用 groovy DSL 来定义数据集,这使得数据的表示非常紧凑,并且可以做一些很酷的事情,比如生成随机数据,因为它只是 groovy 代码。

于 2014-11-11T01:35:54.260 回答
1

DBUnit 的情况有时确实令人沮丧。Marc Philipp使用dbunit-datasetbuilder解决了一些问题,特别是如果您将它与处于早期阶段的验证器结合使用。您可以在SZE看到它的实际应用。

免责声明:所有引用的 github 资源均由我维护。

于 2016-01-09T10:58:58.780 回答
0

可以在此处找到使用Spring配置和Specs2测试的替代方法

于 2013-04-08T16:42:51.710 回答
0

我刚刚发布了一个基于 DSL 的框架,称为踏板加载器,可通过github获得。文档在这里

它允许您直接使用 JPA 实体级抽象。由于它是一个 groovy 脚本,因此您可以使用所有 groovy 结构。

要将行插入到由名为 Student 的 JPA 实体支持的表中,其中包含名为 id、name 和grade 的字段(不是数据库列,而是映射字段),您可以执行以下操作:

allStudents = table(Student, ['id', 'name', 'grade']) {
    row 1, 'Joe', Grade.A
    rowOfInterest = row 2, 'John', Grade.B
}

Grade 是 Student 类中的一个枚举,它映射到数据库列(可能使用 JPA 2.1 @Convert 注释)。allStudents 是一个包含行的列表,rowOfInterest 是对特定行的引用。这些属性(allStudents 和 rowOfInterest)可用于您的单元测试。

于 2015-01-02T15:22:57.607 回答