代码之家  ›  专栏  ›  技术社区  ›  user14070

集成测试所需的数据库数据;由API调用创建还是使用导入的数据?

  •  63
  • user14070  · 技术社区  · 17 年前

    这个问题或多或少与编程语言无关。然而,由于这些天我主要研究Java,所以我将从中提取我的例子。我也在考虑OOP的情况,所以如果你想测试一个方法,你需要一个该方法类的实例。

    A. 核心规则 unit tests 它们应该是自治的,这可以通过将类与其依赖项隔离来实现。有几种方法可以做到这一点,这取决于你是否使用 IoC (在Java世界中,我们有Spring、EJB3和其他提供注入功能的框架/平台)和/或如果你模拟对象(对于Java,你有 JMock EasyMock )将正在测试的类与其依赖项分开。

    如果我们需要测试不同类中的方法组,并查看它们是否很好地集成,我们可以编写 integration tests 。这是我的问题!

    • 至少在web应用程序中,状态通常会持久化到数据库中。我们可以使用与单元测试相同的工具来实现独立于数据库。但在我看来,我认为在某些情况下,不使用数据库进行集成测试是一种过分的嘲弄(但也可以不同意;根本不使用数据库也是一个有效的答案,因为它使问题变得无关紧要)。
    • 当您使用数据库进行集成测试时,如何用数据填充该数据库? 我可以看到两种方法:
      • 存储集成测试的数据库内容,并在开始测试之前加载它。如果它是以SQL转储、数据库文件、XML或其他方式存储的,那么我们很想知道。
      • 通过API调用创建必要的数据库结构。在测试代码中,这些调用可能分为几个方法,每个方法 五月 失败。这可以被视为您的集成测试依赖于其他测试。

    您如何确保测试所需的数据库数据在需要时存在?你为什么选择你选择的方法?

    请提供一个带有动机的答案 ,因为有趣的部分在于动机。记住,仅仅说“这是最好的做法!”并不是 真实的 动机,它只是重复你从别人那里读到或听到的东西。对于这种情况,请解释 为什么? 这是最好的做法。

    *我在单元测试的定义中包含了一个方法,该方法在同一类的(相同或其他)实例中调用其他方法,即使从技术上讲可能不正确。请随时纠正我,但让我们把它作为次要问题。

    9 回复  |  直到 17 年前
        1
  •  55
  •   Esko Luontola    17 年前

    我更喜欢使用API调用来创建测试数据。

    在测试开始时,您创建一个空数据库(内存中或生产中使用的数据库),运行安装脚本对其进行初始化,然后创建数据库使用的任何测试数据。测试数据的创建可以通过以下方式组织 Object Mother 模式,以便在许多测试中可以重用相同的数据,可能会有微小的变化。

    您希望在每次测试之前使数据库处于已知状态,以便进行可重复的测试而没有副作用。因此,当测试结束时,您应该删除测试数据库或回滚事务,以便下一个测试可以始终以相同的方式重新创建测试数据,而不管之前的测试是通过还是失败。

    我之所以避免导入数据库转储(或类似转储),是因为它将测试数据与数据库模式耦合。当数据库模式更改时,您还需要更改或重新创建测试数据,这可能需要手动操作。

    如果测试数据是在代码中指定的,那么您将拥有IDE重构工具的强大功能。当您做出影响数据库模式的更改时,它可能也会影响API调用,因此您无论如何都需要使用API重构代码。通过几乎相同的努力,您还可以重构测试数据的创建,特别是如果重构可以自动化(重命名、引入参数等)。但是,如果测试依赖于数据库转储,那么除了重构使用API的代码之外,还需要手动重构数据库转储。

    与数据库集成测试相关的另一件事是测试从以前的数据库模式升级是否正常。为此,你可能想读这本书 Refactoring Databases: Evolutionary Database Design 或者这篇文章: http://martinfowler.com/articles/evodb.html

        2
  •  6
  •   Franci Penov    17 年前

    为什么这两种方法被定义为排他性的?

    • 我看不出任何可行的论据 使用预先存在的数据集,特别是具有 造成了过去的问题。

    • 我不能 看到任何可行的论据 通过编程扩展该数据 所有可能的条件 你可以想象造成问题 甚至a 用于积分的随机数据位 测试 .

    在现代 agile approaches ,单元测试是真正重要的地方,每次运行相同的测试。这是因为单元测试的目的不是发现bug,而是在开发过程中保留应用程序的功能,允许开发人员根据需要进行重构。

    另一方面,集成测试旨在发现您没有预料到的错误。跑步 一些 在我看来,每次不同的数据甚至可能是好的。如果你失败了,你只需要确保你的测试保留了失败的数据。记住,在正式的集成测试中,除了bug修复外,应用程序本身将被冻结,因此您的测试可以更改为测试最大可能数量和种类的bug。在集成中,你可以而且应该把厨房水槽扔到应用程序上。

    当然,正如其他人所指出的那样,所有这些自然取决于你正在开发的应用程序的类型和你所在的组织的类型等。

        3
  •  5
  •   Joe Soul-bringer    17 年前

    在集成测试中,您需要使用真实的数据库进行测试,因为您必须验证您的应用程序是否真的可以与数据库通信。将数据库隔离为依赖关系意味着您推迟了对数据库是否正确部署、架构是否符合预期以及应用程序是否配置了正确连接字符串的真正测试。您不希望在部署到生产环境时发现这些问题。

    您还希望使用预创建的数据集和空数据集进行测试。你需要测试两条路径,即你的应用程序从一个只有默认初始数据的空数据库开始,开始创建和填充数据,以及一个针对你想要测试的特定条件(如压力、性能等)的定义良好的数据集。

    此外,在每个状态之前,确保数据库处于已知状态。您不希望集成测试之间存在依赖关系。

        4
  •  4
  •   Don Kirkby    17 年前

    听起来你的问题实际上是两个问题。您是否应该将数据库排除在测试之外?当你创建数据库时,你应该如何在数据库中生成数据?

    如果可能的话,我更喜欢使用实际的数据库。CRUD类中的查询(用SQL、HQL等编写)在面对实际数据库时经常会返回令人惊讶的结果。最好尽早解决这些问题。开发人员通常会为CRUD编写非常精简的单元测试;只检测最良性的病例。使用实际的数据库进行测试可以测试您可能甚至没有意识到的各种极端情况。

    话虽如此,可能还有其他问题。每次测试后,您都希望将数据库恢复到已知状态。我目前的工作是通过执行所有DROP语句,然后从头开始完全重新创建所有表来对数据库进行核处理。这在Oracle上非常慢,但如果你使用像HSQLDB这样的内存数据库,速度可能会非常快。当我们需要解决Oracle特定的问题时,我们只需更改数据库URL和驱动程序属性,然后针对Oracle运行。如果你没有这种数据库可移植性,那么Oracle也有某种数据库快照功能,可以专门用于此目的;将整个数据库回滚到某个先前状态。我肯定其他数据库都有。

    根据数据库中的数据类型,API或加载方法可能会更好或更糟。当您拥有具有许多关系的高度结构化数据时,API将通过明确数据之间的关系使您的生活更轻松。在创建测试数据集时,您将更难出错。正如其他海报所提到的,重构工具可以自动处理数据结构的一些变化。我经常发现将API生成的测试数据视为构成场景是有用的;当用户/系统完成步骤X、Y和Z后,测试将从那里开始。这些状态可以实现,因为您可以编写一个程序来调用用户将使用的相同API。

    当您需要大量数据时,加载数据变得更加重要,您的数据中几乎没有关系,或者数据中存在无法使用API或标准关系机制表示的一致性。在我团队的一份工作中,我正在为一个大型网络数据包检测系统编写报告应用程序。当时的数据量相当大。为了触发有用的测试用例子集,我们确实需要嗅探器生成的测试数据。这样,关于一个协议的信息之间的相关性将与关于另一协议的信息相关联。很难在API中捕捉到这一点。

    大多数数据库都有导入和导出表的分隔文本文件的工具。但通常你只想要其中的子集;使得使用数据转储变得更加复杂。在我目前的工作中,我们需要对Matlab程序生成并存储在数据库中的实际数据进行转储。我们有一个工具,可以转储数据库数据的一个子集,然后将其和“地面实况”进行比较以进行测试。我们的提取工具似乎在不断地被修改。

        5
  •  4
  •   Luciano Brum    6 年前

    我用过 DBUnit 对数据库中的记录进行快照并以XML格式存储。然后,我的单元测试(当它们需要数据库时,我们称之为集成测试)可以在每次测试开始时从XML文件中擦除和恢复。

    我不确定这是否值得付出努力。一个问题是对其他表的依赖性。我们保留了静态引用表,并构建了一些工具来检测和提取所有子表以及请求的记录。我读到有人建议禁用集成测试数据库中的所有外键。这将使准备数据变得更加容易,但您不再检查测试中的任何引用完整性问题。

    另一个问题是数据库模式更改。我们编写了一些工具,可以为拍摄快照后添加的列添加默认值。

    显然,这些测试 慢一点 而不是纯粹的单元测试。

    当你试图测试一些很难为单个类编写单元测试的遗留代码时,这种方法可能是值得的。

        6
  •  3
  •   Community Mohan Dere    6 年前

    我两者都做,具体取决于我需要测试的内容:

    • 我从SQL脚本或数据库转储中导入静态测试数据。这些数据用于对象加载(反序列化或对象映射)和SQL查询测试(当我想知道代码是否会返回正确的结果时)。

      另外,我通常有一些骨干数据(配置、值到名称查找表等)。这些也将在此步骤中加载。请注意,此加载是一个单独的测试(以及从头开始创建数据库)。

    • 当我有修改数据库的代码(对象->数据库)时,我通常会在一个活的数据库(内存中或某处的测试实例中)上运行它。这是为了确保代码正常工作;不要创建任何大量的行。测试后,我回滚事务(遵循测试不得修改全局状态的规则)。

    当然,这条规则也有例外:

    • 我还在性能测试中创建了大量的行。
    • 有时,我必须提交单元测试的结果(否则,测试会变得太大)。
        7
  •  2
  •   Galwegian    17 年前

    我通常使用SQL脚本来填充您讨论的场景中的数据。

    它很简单,很容易重复。

        8
  •  0
  •   Fredrik Jansson    17 年前

    这可能不会回答你所有的问题(如果有的话),但我在一个项目中决定对数据库进行单元测试。就我而言,我觉得数据库结构也需要测试,即我的数据库设计是否提供了应用程序所需的东西。在项目后期,当我觉得DB结构稳定时,我可能会放弃这个。

    为了生成数据,我决定创建一个外部应用程序,用“随机”数据填充数据库,我创建了一个人名和公司名生成器等。

    在外部程序中这样做的原因是: 1.我可以对逐个测试修改的数据重新运行测试,即确保我的测试能够运行多次,并且测试所做的数据修改是有效的修改。 2.如果需要,我可以清理数据库,重新开始。

    我同意这种方法存在一些失败之处,但在我的情况下,由于例如人员生成是业务逻辑的一部分,因此为测试生成数据实际上也在测试该部分。

        9
  •  0
  •   maoyang    5 年前

    我们的团队最近也遇到了同样的问题。

    以前,我们使用specflow进行集成测试。使用specflow,QA可以编写每个测试用例,在其中向DB填充必要的测试数据。

    现在,QA想要使用poster来测试API,他们如何填充数据?一种解决方案是创建Api来填充它们。另一种方法是将Prod的历史数据同步到测试环境。

    一旦我们尝试了不同的解决方案并决定采用哪种解决方案,我将更新我的答案。