【问题标题】:Setting up proper testing for Django for TDD为 Django 设置适当的 TDD 测试
【发布时间】:2011-02-11 10:16:51
【问题描述】:


长期以来,我一直忽略测试我的项目的需要。

所以我花了一天多的时间寻找为我当前的应用程序实施测试的方法,并试图让一些 TDD 用于新的应用程序。

我找到了很多“教程”,步骤如下:“1. 安装这个 2. 安装那个 3. 安装这个那个 4. 完成!”,
但似乎没有人谈论如何构建测试,无论是文件还是代码。

从来没有人谈论过如何设置 CI 服务器,或者只是将测试与项目部署相结合。
很多人都提到了fabric、virtualenv 和nose——但没有人描述它们是如何将它们作为一个整体一起工作的。

我一直在寻找有关如何通过测试和 CI 等设置适当的 Rails 环境的详细信息...

有没有其他人觉得 Django 社区在这方面缺乏,还是只有我一个人? :)

哦,还有其他人对如何操作有任何建议吗?

【问题讨论】:

  • 不幸的是它的硬话题。我花了一整天时间创建工作 CI 环境。我完成的是:测试、测试覆盖率、pylint、应用程序演示。
  • 只属于你 ;) 请参阅 unittest2、mock、coverage 和 Jenkins。
  • mkelley33:我并不是说缺少工具,而是关于如何一起使用它们的说明。我应该澄清我的问题吗?
  • 我同意这个话题很难并且充满危险。 Django 测试运行程序与“unittest2”相结合是远远不够的。这对于编写一大堆集成测试来说很好,但是这样的测试不是好的单元测试(它们实际上会命中数据库,这很慢,是单元测试的主要失败,所以你没有易于编写的快速- 运行提供良好覆盖率的测试。)此外,它们不会端到端地运行您的整个应用程序(例如,在新进程中或使用 Selenium。)因此它们永远无法证明您的应用程序确实有效 - 主要失败用于验收测试。
  • PyCon 2012 的 Carl Meyer(Pip 和 Virtualenv 的维护者,也没有 Django 懒散)对 Django 中的测试进行了很好的讨论:pyvideo.org/video/699/testing-and-django

标签: django testing tdd continuous-integration bdd


【解决方案1】:

在我看来,这个问题有几个部分。

您需要的一件事是良好的单元测试。单元测试的主要特点是速度非常快,因此可以测试函数输入和分支覆盖的组合可能性。为了获得它们的速度,并保持测试之间的隔离,即使它们是并行运行的,单元测试也不应该接触数据库、网络或文件系统。这样的测试很难在 Django 项目中编写,因为 Django ORM 使得在整个产品代码中分散数据库访问调用变得非常方便。因此,任何对 Django 代码的测试都不可避免地会影响数据库。理想情况下,您应该通过将产品代码中的数据库访问限制为构建在 django ORM 之上的精简数据访问层来解决此问题,该层公开了与您的应用程序相关的方法。另一种方法是让您的测试模拟 ORM 调用。在最坏的情况下,您将放弃这一点:您的单元测试变成了集成测试:它们实际上会影响数据库,跨越架构的多个层,并且需要几分钟才能运行,这会阻碍开发人员足够频繁地运行它们。

这意味着编写集成测试很容易 - Django 测试的规范风格完美地涵盖了这一点。

问题的最后也是最困难的部分是运行验收测试。验收测试的定义特征是它们端到端调用您的应用程序,就像用户在生产中所做的那样,以证明您的应用程序确实有效。使用 django testrunner 的规范 dhango 测试没有做到这一点。他们实际上并不发出 HTTP 请求(相反,他们检查 url 配置以找出将调用哪些中间件和视图来处理特定请求,然后他们在处理中调用它。)这意味着此类测试不会测试您的webserver 配置,也没有任何 javascript,或者在浏览器中渲染等。要测试这个,你需要 selenium 之类的东西。

此外,我们还有许多服务器端进程,例如 cron 作业,它们使用我们 Django 项目中的代码。涉及这些进程的验收测试应该像 cron 一样调用作业,作为一个新进程。

这两种情况都有一些问题。首先,你不能简单地在 Django 测试运行器下运行这样的测试。如果您尝试这样做,那么您会发现您在测试设置期间编写的测试数据(使用 django 固定装置机制,或者通过在测试中简单地调用“MyModel().save()”)位于您的产品代码在不同的进程中运行的交易不是当事方。因此,您的测试必须先提交他们所做的更改,然后产品代码才能看到它们。这会干扰 Django 的测试运行程序帮助进行的测试之间的清理工作,因此您必须将其切换到不同的模式,该模式执行显式删除而不是回滚。可悲的是,这要慢得多。在昨晚的伦敦 Django 用户组中,几个 Django 核心开发人员向我保证,这种情况还有其他复杂性(我承认我不知道它们是什么),最好不要在Django 测试运行器,但将它们创建为一组完全独立的测试。

如果你这样做,那么你的直接问题是你失去了 Django 测试运行器提供的好处:即它创建一个测试数据库,并在每次测试之间清理它。您将不得不为自己创建一些等效的机制。如果您的产品代码作为测试的一部分被调用,您将需要针对测试数据库运行您的产品代码。您需要绝对确定,如果产品代码作为测试的一部分运行,即使是在生产机器上,它也永远不会意外触及生产数据库,因此这种机制必须绝对安全。例如,忘记在测试设置中设置环境变量不应该导致这方面的问题。

这一切还没有考虑到部署带来的复杂性、将项目的某些部分放在不同的存储库中、相互依赖、创建 pip 可安装包等。

总而言之,我很想听听有人认为他们已经找到了解决这个问题的好方法。正如一些评论者所暗示的那样,这远非一个微不足道的问题。

【讨论】:

  • 首先,很好的回复!由于上述确切原因,我们最终进行了大部分集成测试,并且需要几分钟才能运行 :) 我很想将测试 envs/ci envs 与其他 djangonauts 进行比较,看看有什么样的解决方案,如果有人有找到了解决上述问题的方法。
  • 随后的聊天让我相信,人们不应该费心在验收测试之间清理数据库的内容——它们应该以不关心先前内容的方式编写的表。例如,“添加用户”的验收测试不应该关心其他用户是否已经存在。大概这样的测试应该为每次测试运行生成一个新的、唯一的用户名。
  • 我很晚才意识到解决其中一些问题的一种方法是使用称为干净或六边形或端口'n'适配器的架构模式编写应用程序。如果您的核心业务逻辑不包含对外部系统(包括数据库、网络、文件系统)的依赖,那么您可以对它进行彻底的单元测试而无需模拟,然后需要一组大大减少的端到端验收测试来验证集成。
【解决方案2】:

Harry Percival 正在创建一个 Django / TDD / Selenium 教程(如果你住在伦敦,还有相关的研讨会。)他的书读起来就像一个动手教程,并且非常详细地介绍了这个主题:

https://www.obeythetestinggoat.com/book/part1.harry.html

【讨论】:

    【解决方案3】:

    根据我的经验,Web 应用程序的细粒度单元测试不值得,设置/拆卸成本太高,测试也太脆弱。唯一的例外是隔离组件,尤其是那些输入输出清晰、算法复杂的组件。对这些细节进行单元测试。

    我使用名为testbrowser 的半功能测试工具获得了最好的测试体验,该工具在 Python 中模拟浏览器操作。要与 Django 集成,请安装 homophony 应用程序(免责声明:我是该应用程序的作者)。

    Testbrowser 对于测试驱动开发来说可能有点过于粗糙,但它是我迄今为止使用过的最好的测试工具。最重要的是,它可以很好地扩展,而单元测试和基于浏览器的功能测试工具往往会随着应用程序大小的增长而变得非常脆弱。

    至于 CI 工具,请使用 Buildbot 或 Jenkins。

    【讨论】:

    • 如果测试有昂贵的设置和拆卸(例如整个测试需要超过一毫秒),那么它们不是单元测试。
    • 我在测试中看到的最常见的错误是未能确保每个测试都是单元测试或端到端测试。许多项目最终都进行了混合测试,介于两者之间。这种混合测试没有继承单元测试或端到端测试的任何优点,而是继承了单元测试和端到端测试的所有主要弱点。这对大笔成本几乎没有回报,导致错误的结论是测试不值得。
    【解决方案4】:

    我结合使用 Django 的 excellent extension 的 python unittest 框架来测试 api 的 /models / helper 函数,并使用 selenium 进行浏览器测试。 Selenium 对如何在 python 中设置和编写测试有很好的说明。

    【讨论】:

      猜你喜欢
      • 2011-02-23
      • 2017-04-06
      • 1970-01-01
      • 2018-03-12
      • 1970-01-01
      • 1970-01-01
      • 2017-08-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多