【问题标题】:How can we make Django tests faster?我们如何让 Django 测试更快?
【发布时间】:2014-05-01 23:27:53
【问题描述】:

我们在 Ubuntu 12.04 上使用 Django 1.4 和 PostgreSQL。我们有很多测试,问题是运行测试非常慢,我认为因为对于每个测试,数据库都是从头开始创建的。我想通过在内存(而不是硬盘)中使用数据库运行测试来加快测试速度。我该怎么做?有链接或者教程吗?

【问题讨论】:

标签: django postgresql django-testing in-memory-database django-tests


【解决方案1】:

在 Django 1.9 中,如果你有一个多核处理器,一个很好的选择就是 flag:

--parallel

这需要您pip install tblib,但可以让您在多个内核上同时运行单元测试。 (https://docs.djangoproject.com/en/1.10/ref/django-admin/#cmdoption-test-parallel)

Django 1.8+ 的另一个不错的选择是标志:

--keepdb

它会重用您的测试数据库,从而避免您每次运行测试时创建新的测试数据库所导致的漫长等待时间。 (https://docs.djangoproject.com/en/1.10/ref/django-admin/#cmdoption-test-keepdb

【讨论】:

  • 值得一提的是,虽然--keep-db 使测试更快,但它只能在本地使用,实际测试(例如您在 CICD 中运行的测试)不应该使用它。它可能会阻止您发现一些问题,尤其是与模型和迁移相关的问题。
【解决方案2】:

最好的选择是为您的测试提供一个单独的设置文件。在 settings_test.py 你告诉它使用默认使用内存数据库的 sqlite:

from base_settings import *

DATABASES = {
  'default': {
    'ENGINE': 'django.db.backends.sqlite3',
    'NAME': ':memory'
  }
}

然后通过添加 --settings=settings_test 来运行您的测试

另请参阅 Django 文档:
https://docs.djangoproject.com/en/dev/topics/testing/overview/#the-test-database

【讨论】:

  • -1。使用 SQLite 进行测试有什么意义(假设您没有使用任何它无法理解的东西)?如果您随后使用 Postgres 进行部署,您如何确定您的数据库相关脚本正在使用您的测试?
  • Uri 询问如何使用内存数据库优化他的测试,使用 sqlite 内存数据库进行测试是 Django 中的标准方法。如果他正在使用 Django,那么他会假设他正在使用 Django ORM,这意味着他的大部分应用程序应该与数据库无关(因此使用 sqlite 进行测试和使用 postgres 进行部署不是问题 - 实际上这是一种常见的做事方式)。参见例如seanhayes.name/2012/11/28/ways-speed-django-tests
  • 根据我的经验,在内存中运行 SQLite 测试实际上通常会使运行单个测试需要更长的时间。特别是如果你有很多迁移,使用内存中的 SQLite 意味着你需要在每次测试运行时重做整个迁移。在持久的 Postgres 实例中使用 --keepdb 运行通常与内存中的瞬态 SQLite 一样快或更快。
【解决方案3】:

有几个有用的 SO 线程:

我确实使用 SQLite 技巧来进行完整性检查,但如果您正在做任何特定于数据库的事情,它会让您发疯:某些 SQL 差异,差异是数据精度等。它也削弱了测试的意义:如果您使用测试来向您保证更改一旦推送到生产环境就会起作用,那么针对不同的数据库运行它们并不是一个好方法。尽可能尝试使用鼻子跳过数据库重新创建并优化您的本地 Postgres 设置。你可以试试avoiding the DB altogether as well.

最适合我的事情是尝试将测试造成的停机时间视为提出更好更改的机会,并鼓励我在启动测试运行程序之前思考我正在更改的内容。

【讨论】:

    【解决方案4】:

    快进到 2016 年,我们在 manage.py 中有一个很好的 option 来加快测试速度。

    --keepdb, -k¶ Django 1.8 中的新功能。在测试运行之间保留测试数据库。这具有跳过创建和 破坏可以大大减少运行测试时间的操作, 尤其是大型测试套件中的那些。如果测试数据库没有 存在,它将在第一次运行时创建,然后为每个 后续运行。任何未应用的迁移也将应用于 在运行测试套件之前测试数据库。

    如果您不使用 TransactionTestCase 及其子类,则大部分测试运行时间将来自数据库创建。如果您有大量迁移,那将非常糟糕。但是你用

    来避免这一切
     ./manage.py test -k myapp
    

    【讨论】:

      【解决方案5】:

      您可以简单地将用于测试的数据库更改为 sqlite:

      import sys
      if 'test' in sys.argv:
          DATABASES['default']['engine'] = 'sqlite3'
      

      请注意,由于数据库之间的某些不兼容,您的某些测试可能会失败,但通常这应该有效。

      【讨论】:

      • -1。使用 SQLite 进行测试有什么意义(假设您没有使用任何它无法理解的东西)?如果您随后使用 Postgres 进行部署,您如何确定您的数据库相关脚本正在使用您的测试?
      • 好吧,如果您有任何与数据库相关的脚本,这将不起作用,但对于简单/常规用例,这是快速且可靠的。如果您担心这两个数据库引擎之间的差异,您可以随时在 Postgres 上运行测试以检查是否有任何差异。
      • 反对反对票,因为它通常是业务逻辑,而不是需要测试的 ORM。当然,原始数据库特定的 SQL 可能会中断,但这是相当边缘的情况;这必须是手工制作的,你应该知道你在做什么。每次单元测试运行节省 8 秒(每个开发人员每天十次!)立即为您节省更多资源。
      【解决方案6】:

      不熟悉 python 或 Django,但从概念上讲,您应该能够:

      1. 在您引导测试时一次创建您的数据库并加载您的设备
      2. 定义保存点。
      3. 在每个测试或一组测试之后,回滚到该保存点。

      (如果 ORM 不支持保存点,您可能需要在测试期间覆盖 ORM 的开始/结束事务代码。)

      http://www.postgresql.org/docs/current/static/sql-savepoint.html

      (我要补充一点,在概念层面上,您的 DBAL 和 ORM 应该在您的测试中被模拟,以便您单独测试您的组件。也就是说,您可能不应该连接到在您的大多数测试中都从数据库开始。)

      【讨论】:

      • 使用回滚的问题在于,任何只发生在单个事务中的测试都不是一个非常彻底的测试。
      • @jjanes:因此保存点而不是单个事务:您可以随意嵌套它们。
      • 但这仍然只适用于单个事务,不是吗?一旦它被提交,它就不能被回滚,并且在它被提交之前,一个边事务是看不到它的。我认为任何完整的测试套件都必须包括多连接可见性/一致性测试。
      • @jjanes:是的,因此需要调整 ORM 代码:在测试期间,而不是发出 beginrollbackcommit 语句,它需要做发出savepoint txrollback to txrelease tx。这样,它就永远不会真正承诺。不过,多连接测试很好。
      猜你喜欢
      • 2012-02-15
      • 2019-03-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多