【问题标题】:How to run Django's test database only in memory?如何仅在内存中运行 Django 测试数据库?
【发布时间】:2011-03-06 23:39:23
【问题描述】:

我的 Django 单元测试需要很长时间才能运行,所以我正在寻找加快速度的方法。我正在考虑安装SSD,但我知道这也有缺点。当然,我可以用我的代码做一些事情,但我正在寻找结构修复。即使运行单个测试也很慢,因为每次都需要重建/南迁移数据库。所以这是我的想法...

既然我知道测试数据库总是很小,为什么我不能将系统配置为始终将整个测试数据库保存在 RAM 中?永远不要触摸磁盘。如何在 Django 中配置它?我宁愿继续使用MySQL,因为这是我在生产中使用的,但如果SQLite 3 或其他东西让这变得容易,我会这样做。

SQLite 或 MySQL 是否可以选择完全在内存中运行?应该可以配置 RAM 磁盘,然后配置测试数据库以将其数据存储在那里,但我不确定如何告诉 Django / MySQL 为某个数据库使用不同的数据目录,特别是因为它一直被擦除并重新创建每次运行。 (我在 Mac FWIW 上。)

【问题讨论】:

    标签: python mysql django unit-testing


    【解决方案1】:

    如果您在运行测试时将数据库引擎设置为 sqlite3,Django will use a in-memory database

    我在我的settings.py 中使用这样的代码在运行我的测试时将引擎设置为 sqlite:

    if 'test' in sys.argv:
        DATABASE_ENGINE = 'sqlite3'
    

    或者在 Django 1.2 中:

    if 'test' in sys.argv:
        DATABASES['default'] = {'ENGINE': 'sqlite3'}
    

    最后在 Django 1.3 和 1.4 中:

    if 'test' in sys.argv:
        DATABASES['default'] = {'ENGINE': 'django.db.backends.sqlite3'}
    

    (对于 Django 1.3,后端的完整路径并非绝对必要,但可以使设置向前兼容。)

    如果您遇到南迁移问题,您还可以添加以下行:

        SOUTH_TESTS_MIGRATE = False
    

    【讨论】:

    • 是的,完全正确。我应该把它放在我的答案中!将它与 SOUTH_TESTS_MIGRATE = False 结合起来,您的测试应该会快很多。
    • 真棒。在较新的 django 设置上使用这一行: 'ENGINE': 'sqlite3' if 'test' in sys.argv else 'django.db.backends.mysql',
    • @Tomasz Zielinski - 嗯,这取决于你在测试什么。但我完全同意,最终,有时,您需要使用您的真实数据库(Postgres、MySQL、Oracle ......)运行测试。但是使用 sqlite 在内存中运行测试可以节省大量时间。
    • 我将 -1 反转为 +1:正如我现在所看到的,使用 sqlite 进行快速运行并切换到 MySQL 的速度要快得多,例如最后的每日测试。 (请注意,我必须进行虚拟编辑才能解锁投票)
    • 注意"test" in sys.argv;它可能会在您不希望触发时触发,例如manage.py collectstatic -i testsys.argv[1] == "test" 是一个更精确的条件,不应该有这个问题。
    【解决方案2】:

    我通常为测试创建一个单独的设置文件并在测试命令中使用它,例如

    python manage.py test --settings=mysite.test_settings myapp
    

    它有两个好处:

    1. 您不必在 sys.argv 中检查 test 或任何此类魔术字,test_settings.py 可以简单地是

      from settings import *
      
      # make tests faster
      SOUTH_TESTS_MIGRATE = False
      DATABASES['default'] = {'ENGINE': 'django.db.backends.sqlite3'}
      

      或者您可以根据需要进一步调整它,将测试设置与生产设置完全分开。

    2. 另一个好处是您可以使用生产数据库引擎而不是 sqlite3 运行测试,从而避免细微的错误,因此在开发时使用

      python manage.py test --settings=mysite.test_settings myapp
      

      在提交代码之前运行一次

      python manage.py test myapp
      

      只是为了确保所有测试都真正通过。

    【讨论】:

    • 我喜欢这种方法。我有一堆不同的设置文件并将它们用于不同的服务器环境,但我没有考虑过使用这种方法来选择不同的测试数据库。谢谢你的想法。
    • 嗨 Anurag,我试过这个,但我在设置中提到的其他数据库也被执行了。我无法弄清楚确切的原因。
    • 不错的答案。我想知道在通过覆盖率运行测试时如何指定设置文件。
    • 这是一个好方法,但不是 DRY。 Django 已经知道您正在运行测试。如果你能以某种方式“接触”这些知识,你就会被设置。不幸的是,我认为这需要扩展管理命令。在框架的核心中使其通用化可能是有意义的,例如,每当调用 manage.py 时将设置 MANAGEMENT_COMMAND 设置为当前命令,或者类似的效果。
    • @DylanYoung 您可以通过将主要设置包含在 test_settings 中并覆盖您想要测试的内容来使其干燥。
    【解决方案3】:

    MySQL 支持名为“MEMORY”的存储引擎,您可以在数据库配置 (settings.py) 中将其配置为:

        'USER': 'root',                      # Not used with sqlite3.
        'PASSWORD': '',                  # Not used with sqlite3.
        'OPTIONS': {
            "init_command": "SET storage_engine=MEMORY",
        }
    

    请注意,MEMORY 存储引擎不支持 blob / text 列,因此如果您使用的是 django.db.models.TextField,这将不适合您。

    【讨论】:

    【解决方案4】:

    我无法回答您的主要问题,但您可以采取一些措施来加快速度。

    首先,确保您的 MySQL 数据库设置为使用 InnoDB。然后它可以在每次测试之前使用事务来回滚数据库的状态,根据我的经验,这导致了巨大的加速。您可以在 settings.py 中传递数据库初始化命令(Django 1.2 语法):

    DATABASES = {
        'default': {
                'ENGINE':'django.db.backends.mysql',
                'HOST':'localhost',
                'NAME':'mydb',
                'USER':'whoever',
                'PASSWORD':'whatever',
                'OPTIONS':{"init_command": "SET storage_engine=INNODB" } 
            }
        }
    

    其次,您不需要每次都运行南迁移。在你的 settings.py 中设置SOUTH_TESTS_MIGRATE = False,数据库将使用普通的syncdb创建,这比运行所有历史迁移要快得多。

    【讨论】:

    • 很棒的提示!它将我的测试从369 tests in 498.704s 减少到369 tests in 41.334s 。这速度快了 10 倍以上!
    • 在 Django 1.7+ 中,settings.py 中是否有等效的开关?
    • @EdwardNewell 不完全是。但是您可以使用--keep 来持久化数据库,并且不需要在每次测试运行时重新应用您的完整迁移集。新的迁移仍将运行。如果您经常在分支之间切换,很容易进入不一致的状态(您可以在切换之前通过将数据库更改为测试数据库并运行migrate 来恢复新的迁移,但这有点痛苦)。
    【解决方案5】:

    你可以做双重调整:

    • 使用事务表:将在每个 TestCase 之后使用数据库回滚来设置初始夹具状态。
    • 将您的数据库数据目录放在 ramdisk 上:就数据库创建而言,您将获得很多收益,并且运行测试也会更快。

    我正在使用这两种技巧,我很高兴。

    如何在 Ubuntu 上为 MySQL 设置它:

    $ sudo service mysql stop
    $ sudo cp -pRL /var/lib/mysql /dev/shm/mysql
    
    $ vim /etc/mysql/my.cnf
    # datadir = /dev/shm/mysql
    $ sudo service mysql start
    

    注意,这只是为了测试,从内存中重新启动后你的数据库会丢失!

    【讨论】:

    • 谢谢!为我工作。我不能使用 sqlite,因为我使用的是 mysql 特有的功能(全文索引)。对于 ubuntu 用户,您必须编辑 apparmor 配置以允许 mysqld 访问 /dev/shm/mysql
    • 为 Ivan 和 Potr 的单挑干杯。暂时禁用了 AppArmor mysql 配置文件,但找到了自定义相关本地配置文件的指南:blogs.oracle.com/jsmyth/entry/apparmor_and_mysql
    • 嗯。我已经尝试自定义本地配置文件以让 mysqld 访问 /dev/shm/mysql 路径及其内容,但该服务只能以“抱怨”模式(aa-complain 命令)而不是“强制”启动,对于某些人原因...另一个论坛的问题!我无法理解的是,当它起作用时根本没有“投诉”,这意味着 mysqld 没有违反配置文件......
    【解决方案6】:

    另一种方法:让另一个 MySQL 实例在使用 RAM 磁盘的 tempfs 中运行。本博文说明: Speeding up MySQL for testing in Django

    优点:

    • 您使用与生产服务器完全相同的数据库
    • 无需更改默认 mysql 配置

    【讨论】:

      【解决方案7】:

      根据 Anurag 的回答,我通过创建相同的 test_settings 并将以下内容添加到 manage.py 来简化流程

      if len(sys.argv) > 1 and sys.argv[1] == "test":
          os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.test_settings")
      else:
          os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")
      

      看起来更简洁,因为 sys 已经导入,并且 manage.py 仅通过命令行使用,因此无需混乱设置

      【讨论】:

      • 注意"test" in sys.argv;它可能会在您不希望触发时触发,例如manage.py collectstatic -i testsys.argv[1] == "test" 是一个更精确的条件,不应该有这个问题。
      • @keturn 这样它会在不带参数运行./manage.py 时生成异常(例如查看哪些插件可用,与--help 相同)
      • @AntonyHatchkins 这很容易解决:len(sys.argv) > 1 and sys.argv[1] == "test"
      • @DylanYoung 是的,这正是我希望 Alvin 添加到他的解决方案中的内容,但他对改进它并不是特别感兴趣。无论如何,它看起来更像是一种快速破解,而不是合法的解决方案。
      • 有一段时间没看这个答案,我更新了 sn-p 以反映@DylanYoung 的改进
      【解决方案8】:

      在你的setting.py下面使用

      DATABASES['default']['ENGINE'] = 'django.db.backends.sqlite3'
      

      【讨论】:

        猜你喜欢
        • 2020-05-02
        • 2020-07-02
        • 2013-02-20
        • 1970-01-01
        • 1970-01-01
        • 2011-06-04
        • 2015-04-26
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多