为Django的TDD设置适当的测试

为Django的TDD设置适当的测试,django,testing,tdd,continuous-integration,bdd,Django,Testing,Tdd,Continuous Integration,Bdd,长期以来,我一直忽视测试我的项目的必要性 因此,我花了一天多的时间寻找方法,为我当前的应用程序实施测试,并尝试为新应用程序提供一些TDD 我找到了很多“教程”,包括以下步骤:“1.安装这个2.安装那个3.安装这个4.完成!”, 但似乎没有人谈论如何构建测试,包括文件和代码 也没有人谈论过如何设置CI服务器,或者只是将测试与项目的部署结合起来。 很多人提到了织物、virtualenv和鼻子,但没有人描述它们是如何作为一个整体一起工作的 我不断发现的是关于如何通过测试和CI等设置适当Rails环境的


长期以来,我一直忽视测试我的项目的必要性

因此,我花了一天多的时间寻找方法,为我当前的应用程序实施测试,并尝试为新应用程序提供一些TDD

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

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

我不断发现的是关于如何通过测试和CI等设置适当Rails环境的详细信息

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


哦,还有其他人对如何做有什么建议吗?

根据我的经验,web应用程序的细粒度单元测试不值得,安装/拆卸成本太高,测试太脆弱。唯一的例外是孤立的组件,尤其是那些具有清晰输入和输出以及复杂算法的组件。对这些进行单元测试,以获得最小的细节

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

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


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

我使用PythonUnitTest框架的Django组合来测试api/models/helper函数,使用selenium进行浏览器内测试。Selenium对如何用python设置和编写测试有很好的指导。

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

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

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

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

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

这两种情况都有一些问题。首先,您不能简单地在Django测试运行程序下运行此类测试。如果您尝试这样做,那么您将发现您在测试设置期间编写的测试数据(使用django fixtures机制,或者在测试中简单地调用“MyModel().save()”)位于一个事务中,而您在不同进程中运行的产品代码并不是该事务的一方。因此,您的测试必须在产品代码看到它们之前提交它们所做的更改。这会干扰Django的testrunner所做的测试之间的清理,因此您必须将其切换到不同的模式,该模式执行显式删除而不是回滚。遗憾的是,这要慢得多。在昨晚的伦敦Django用户组会议上,两位Django核心开发人员向我保证,这个场景还有其他复杂问题(我承认我不知道它们是什么),最好不要在Django测试运行程序中运行验收测试,而是将它们创建为一组完全独立的测试来避免

如果您这样做,那么您眼前的问题是您已经失去了Django test runnner提供的好处:即它创建了一个测试数据库,并在每个测试之间清理它。您必须为自己创建一些等效的机制。如果测试数据库作为测试的一部分被调用,您将需要在测试数据库上运行产品代码。您需要绝对确定,如果产品代码作为测试的一部分运行,即使是在生产设备上,那么它永远不会意外地接触到生产数据库,因此这种机制必须是绝对故障安全的。忘了说话