【问题标题】:Why not hit the database inside unit tests?为什么不在单元测试中访问数据库?
【发布时间】:2009-06-28 14:08:54
【问题描述】:

我在博客中读到,当单元测试运行时,不应访问数据库。我理解这个理论,但是说我有复杂的存储过程,这是业务域操作的一部分。我想为与业务操作相关的代码编写一组单元测试,但是如果我模拟数据库,我感觉我没有“真正”测试作为操作一部分的所有部分。例如,有人可以在其中一个数据库代码中创建一个错误,并且测试仍然可以正常运行。

我想知道这个关于单元测试的指南在实践中是否很好。 我已经看到了“集成测试”的概念,但是我不确定使用什么工具来进行集成测试。例如 ¿ 是否可以使用 Nunit 之类的测试框架创建集成测试?

谢谢

雨果

【问题讨论】:

    标签: unit-testing nunit


    【解决方案1】:

    你只是处于语义灰色地带。

    • 系统测试从端到端覆盖整个系统。
    • 单元测试可用于描述端到端循环的子部分。

    在这种情况下,您的应用程序代码的单元测试将/可能不会命中数据库,但您将/可能有涵盖数据库存储过程的单元测试...

    基本上将您的应用程序划分为沿着有意义的分区进行测试的事物。如果您选择了错误的分区线,您最终会遇到模拟对象和测试脚手架和其他东西的大代码维护问题......

    Web 应用程序的一种常见方法是编写一系列单元测试来测试数据访问层...

    然后编写一系列单元测试,测试应用层(包括数据层)……

    最后写一些基于浏览器的系统测试...

    诀窍是仅通过 API 将信息输入和输出中间集(应用层),而不是钻入数据库以查看是否有效。这样,如果您更改数据架构,您的测试就不会中断。

    但有时(正如我在写这篇文章时实际所做的那样)您必须查看数据库以制作有意义且健壮的测试套件(我正在测试服务器-服务器传输协议)。

    专注于为您的应用程序获得最大的代码覆盖率和稳定性,同时编写最少数量的测试脚手架,并避免测试套件中的脆弱性。

    【讨论】:

    • 我无法告诉你我说了多少次几乎完全相同的事情。到我们的问答组!!!他们看到一个大测试,有时不明白存在依赖关系。我遇到过一些变形,他们不能多次添加相同的记录。然后我必须让他们相信他们需要在测试中添加设置和拆卸部分。 (一个卑微的程序员/设计师如何敢于告诉他们如何编写测试
    • 你写的测试太棒了(虽然我个人喜欢测试)
    【解决方案2】:

    单元测试未命中数据库的主要原因有两个:

    • 您希望尽可能快地进行测试
    • 您希望将代码作为一个单元进行测试,而不是如何与其他代码/数据库集成。

    在您的情况下,您需要测试存储过程。然后您需要编写一个运行这些存储过程的测试。您可以通过代码运行它们(集成测试),也可以直接从测试中运行它们(某种单元测试)。在这两种情况下,您都可以使用 Nunit。

    【讨论】:

      【解决方案3】:

      好吧,问题是,如果您的单元测试命中数据库,那么您不仅要测试单个代码单元,而是要测试多个代码单元——还有 DAL、存储过程等。这就是为什么要使用 TDD支持者说不要在单元测试中访问数据库。

      这也是为什么你不应该在纯粹的、TDD 概念化的、理论上的神圣Unit Tests 中占有太多的权重。虽然单元测试本身并不是一件坏事,实际上对于确保程序员正确构建每个组件非常重要 - 系统测试更为重要。总体而言,可能更难找到导致系统测试失败的确切方法,它们更现实并且实际测试系统
      所谓的“组件测试”——更像是系统测试,但只针对系统的一小部分,端到端——在我看来是一个很好的折衷方案。

      (现在提示 TDD 的反驳... :))

      【讨论】:

        【解决方案4】:

        写完Should one test internal implementation, or only test public behaviour?,讨论了大家的答案,我觉得答案可能取决于开发团队有多少人。

        使用数据库进行测试的优势:

        • 测试更真实(使用实际数据库)
        • 需要编写的测试代码更少(不需要编写数据库的模拟)

        用数据库测试的缺点:

        • 在编写数据库访问层之前,您的测试无法运行
        • 如果有错误或测试失败,您不知道错误是在您的代码中还是在数据库访问层中

        如果(但也许仅当)编写上层的人与编写数据库访问层的人不同时,缺点尤其重要。

        【讨论】:

          【解决方案5】:

          YMHO 这里有一点语义问题和一点技术问题。

          单元测试应该单独测试一小部分代码,以便在没有其他代码的情况下对其进行检查。失败的单元测试应该意味着只有一小部分代码需要被检查纠正(通常是实现功能的代码和单元测试本身)。

          存在其他测试范围以检查整个系统的功能(系统测试),或用于验证完整功能的功能测试,或用于检查交互的集成测试,用于检查是否存在已纠正测试的回归测试等。

          必须为每个项目制定测试策略,但总的来说,拥有不同的类别会有很大帮助。所以单元测试应该限制在测试尽可能小的单元代码,并使用其他测试程序来测试功能、集成等。

          语义部分是正确使用单元测试库来执行一些功能测试,我想如果你让你的套件将严格的单元测试和功能测试分开,这没什么大不了的(即使他们共享测试工具)

          【讨论】:

            【解决方案6】:

            在测试中处理数据库的问题是数据库中的更改或代码中的更改可能会使测试失败。单元测试通常应该尽可能直接地指出导致他失败的变化。 因此,如果您想测试数据库访问的代码,可以使用固定的测试数据库。通过这种方式,您可以确定访问数据库的代码是错误的,因为您知道数据库没有更改,如果您想更改数据库设置,请先更改测试数据库并看到您的测试失败,然后修复他,然后更改不测试数据库。 在我参与的一个项目中,我们创建了一个小型数据库,该数据库在每次测试之前生成到内存中。

            【讨论】:

              【解决方案7】:

              “...当单元测试运行时,不应访问数据库...” - 当然,除非您正在对持久性对象进行单元测试。

              我不知道您引用了哪些博客,但我敢打赌他们所说的是在集成测试期间模拟某些组件会带来好处。如果您已经对持久层进行了单元测试,则无需再次对其进行测试。在这种情况下,模拟的好处更多地与减少依赖性和不确定性有关。例如,您可能已经对代码进行了单元测试,将其打包,现在是时候将其移至更接近生产的环境中了。如果您需要的数据库不可用(例如,没有正确版本的模式、缺少您需要的数据,或者只是被另一个首先到达那里的应用程序需要),您可以使用模拟来允许您的集成立即进行测试。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2015-11-27
                • 2013-07-04
                • 2011-11-19
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2010-11-21
                • 1970-01-01
                相关资源
                最近更新 更多