6

     在我们开始之前,我知道很少有人认为命中数据库的测试不是“单元测试”。也许“集成测试”会是一个更好的名字。无论哪种方式,开发人员都会对数据库进行测试。

为了启用单元测试,我有一个开发人员本地数据库,我在每次测试开始时使用dbUnit     清除并填充一组已知的数据。这一切都很好,直到测试使用的表以某种方式发生变化,我必须手动更新所有 XML 数据集。这是一种痛苦。我认为其他人一定遇到了同样的问题,并希望找到一个很好的解决方案。那么对于需要填充数据库的测试,您使用什么以及如何处理表定义的更改?(虽然我使用 Java,但我对使用不同技术的解决方案持开放态度。)

编辑: 澄清一点。我有一个人为的测试,例如:

void testLoadRevision() {
    database.clear(); // Clears every table dbUnit knows about.
    database.load("load/trevision.xml", "load/tissue.xml");
    SomeDatabaseThingie subject = new SomeDatabaseThingie(databaseProvider);
    Revision actual = subject.load();
    assert(actual, expected);
}

在那我有两个表 - tRevision 和 tIssue。加载的修订版使用来自 tIssue 的少量数据。后来 tIssue 获得了一个修订版不关心的新字段。由于新字段为“非空”并且没有合理的默认值,因此该测试将失败,因为 tIssue.xml 将无效。

通过像这样的小改动,编辑 tIssue 并不难。但是,当 XML 文件的数量随着每个流程开始激增时,它就变成了大量的工作。

干杯,
    mlk

4

3 回答 3

3

我认为这个问题的答案分为两个阶段:

模式只有一个权威定义

对于数据库的外观应该只有一个定义。在正常情况下,我更喜欢使用 SQL DDL 脚本来指定数据库的架构。

单元测试应该使用与应用程序相同的数据库模式的权威定义,并且它应该在测试运行之前基于该定义创建数据库,并在测试运行之后再次将其完全删除

也就是说,工具可能与模式不同步,您将需要手动更新工具生成的东西。例如,我使用 .NET 的实体框架,它根据数据库模式自动生成类。当我更改架构时,我需要手动告诉我的工具更新这些类。这很痛苦,但我不知道有什么办法可以解决,除非工具支持自动化。

每个测试都应该从空数据开始

每个测试都应该从没有任何数据的数据库开始。每个测试都应该只填充执行测试所需的数据,完成后,它应该再次清理数据库。

您目前正在做的事情听起来像是一种称为 General Fixture 的反模式,您尝试在其中预加载一组数据,这些数据代表尽可能广泛的一组场景。但是,这使得测试互斥条件变得非常困难,并且如果您在某些测试中修改此预加载数据,也可能导致测试相互依赖。

这在优秀的书xUnit Test Patterns中得到了很好的解释。

于 2009-08-28T10:12:58.120 回答
3

好吧,在我看来,这是一个结合已经存在的东西的问题。

上面描述的场景:

  1. 编写数据库迁移
  2. 应用数据库迁移(在测试运行开始时手动或自动)
  3. 观察您的测试因违反约束而中断(非空)

您可以扩展它,以便您成为一个执行以下操作的小程序:

  1. 使用 DbUnit XML 填充数据库
  2. 应用数据库迁移
  3. 在 DbUnit XML(以及可选的 DTD)中就地提取数据库的内容(参见 DbUnit 主页 -> DbUnit 常见问题解答 -> 如何从我的数据库中提取平面 XML 数据集?)
  4. 将更新后的 DbUnit XML(和 DTD)检查到源代码控制中。

对于应用迁移,我衷心推荐Flyway。它支持 Sql(带占位符替换)和基于 Java 的迁移。然后,您可以使用 Maven 插件或使用 API 以编程方式应用迁移。后者非常适合这种情况。

完整的工作流程则变为:

  1. 编写数据库迁移
  2. 执行您的 DbUnitXmlDtdUpdater 程序
  3. 观察你的单元测试通过

快乐的时光,

阿克塞尔

免责声明:我是 Flyway 的开发人员之一。

于 2010-08-11T07:19:07.553 回答
2

我有同样的 dbunit xml 平面文件在需要数据更改的数据库模式演变时运行不同步的问题(即使对于像添加强制列这样简单的事情)。

虽然使用一些手写脚本转换所有 xml 文件是一种选择,但我仍然认为应该在不同的抽象级别上解决这个问题,更类似于处理实时数据的方式:进化数据库设计。

数据库迁移工具已经知道 delta 脚本,因此拥有一种 dbunit 适配器会很棒。

如果发现以下涵盖该问题的博客条目:http: //blog.liquibase.org/2007/06/unit-testing-the-database-access-layer.html

为了解决保持测试数据定义与模式不同步的问题,您需要将测试数据与数据库一起构建,以便通过在最初创建后进行的数据库重构对其进行修改。[..] 通过在您的数据库更改中包含测试数据,数据会自动以与生产数据相同的方式保存。在每个方法的数据集上使用这种技术还具有性能更好的优势,因为数据只插入一次,......

但他自己补充说:

但它的缺点是您需要在一个地方处理任何方法都需要的所有测试数据。

...我猜这对于更复杂的场景是不可能的。他继续说:

为了促进这项技术,我在 LiquiBase 中构建了执行上下文的概念,这样您就可以标记测试数据的更改,并仅在运行单元测试的环境中应用它们。到目前为止,我对结果感到满意。当数据库架构与代码预期的内容之间存在差异时,或者当我的 SQL 中存在错误并且我没有因数据库重构而丢失任何测试时,测试会失败。

这是链接:www.liquibase.org/manual/contexts,但这至少不是我想要的,虽然我可以将我的测试数据暴露给数据库迁移工具,但我仍然喜欢让它非常接近数据库测试。

有人想吗?

于 2010-08-10T16:10:06.517 回答