Как запустить тестовую базу данных Django только в памяти?

107

Мои модульные тесты Django занимают много времени, поэтому я ищу способы ускорить это. Я рассматриваю возможность установки SSD, но я знаю, что это тоже имеет свои недостатки. Конечно, есть вещи, которые я мог бы сделать с моим кодом, но я ищу структурное исправление. Даже запуск одного теста происходит медленно, так как база данных необходимо перестраивать/перенаправлять на юг каждый раз. Так вот моя идея...

Поскольку я знаю, что тестовая база данных всегда будет довольно маленькой, почему я не могу просто настроить систему, чтобы всегда хранить всю тестовую базу данных в ОЗУ? Никогда не касайтесь диска вообще. Как настроить это в Django? Я бы предпочел использовать MySQL, так как это то, что я использую в производстве, но если SQLite 3 или что-то еще облегчает это, я бы пошел таким образом.

Имеет ли SQLite или MySQL возможность полностью работать в памяти? Должно быть возможно настроить RAM-диск, а затем настроить тестовую базу данных для хранения там своих данных, но я не уверен, как сообщить Django/MySQL использовать другой каталог данных для определенной базы данных, тем более, что он продолжает стираться и воссоздал каждый прогон. (Я нахожусь на Mac FWIW.)

Теги:
unit-testing

7 ответов

144
Лучший ответ

Если вы установите свой движок базы данных на sqlite3 при выполнении своих тестов, Django будет использовать базу данных в памяти.

Я использую такой код в моем 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
  • 0
    Вы помещаете это в свои settings.py?
  • 9
    Да, точно. Я должен был поставить это в своем ответе! Добавьте к этому SOUTH_TESTS_MIGRATE = False, и ваши тесты должны быть намного быстрее.
Показать ещё 18 комментариев
74

Обычно я создаю отдельный файл настроек для тестов и использую его в тестовой команде, например.

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

Он имеет два преимущества:

  • Вам не нужно проверять test или любое такое волшебное слово в sys.argv, test_settings.py может просто быть

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

    Или вы можете дополнительно настроить его для своих нужд, чисто отделяя тестовые настройки от производственных настроек.

  • Еще одно преимущество заключается в том, что вы можете запускать тест с помощью ядра базы данных вместо sqlite3, избегая тонких ошибок, поэтому при разработке используйте

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

    и перед выполнением кода запускается один раз

    python manage.py test myapp
    

    чтобы убедиться, что все тесты действительно проходят.

  • 2
    Мне нравится этот подход. У меня есть куча разных файлов настроек, и я использую их для разных серверных сред, но я не думал об использовании этого метода для выбора другой тестовой базы данных. Спасибо за идею.
  • 0
    Привет Anurag, я попробовал это, но другие мои базы данных, упомянутые в настройках, также выполняются. Я не могу понять точную причину.
Показать ещё 7 комментариев
23

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, это не сработает для вас.

  • 5
    +1 за упоминание об отсутствии поддержки столбцов blob / text. Похоже, что он также не поддерживает транзакции ( dev.mysql.com/doc/refman/5.6/en/memory-storage-engine.html ).
  • 0
    Если вам действительно нужны тесты в памяти, вам, вероятно, лучше использовать sqlite, который по крайней мере поддерживает транзакции.
15

Я не могу ответить на ваш главный вопрос, но есть несколько вещей, которые вы можете сделать, чтобы ускорить процесс.

Во-первых, убедитесь, что ваша база данных MySQL настроена на использование InnoDB. Затем он может использовать транзакции для отката состояния db перед каждым тестом, что по моему опыту привело к значительному ускорению. Вы можете передать команду инициализации базы данных в файле 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" } 
        }
    }

Во-вторых, вам не нужно каждый раз запускать южные миграции. Установите SOUTH_TESTS_MIGRATE = False в свои settings.py, и база данных будет создана с простой синхронизацией, которая будет намного быстрее, чем выполнение всех исторических миграций.

  • 0
    Отличный совет! Это сократило мои тесты с 369 tests in 498.704s до 369 tests in 41.334s . Это более чем в 10 раз быстрее!
  • 0
    Есть ли эквивалентный переключатель в settings.py для миграции в Django 1.7+?
Показать ещё 1 комментарий
9

Вы можете выполнить двойную настройку:

  • использовать транзакционные таблицы: состояние исходных состояний будет установлено с помощью отката базы данных после каждой тестовой системы.
  • поместите данные базы данных данных в ramdisk: вы получите гораздо больше, чем создание базы данных, а также запуск теста будет быстрее.

Я использую оба трюка, и я очень доволен.

Как настроить его для MySQL на Ubuntu:

$ 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

Остерегайтесь, это просто для тестирования, после перезагрузки ваша база данных из памяти потеряна!

  • 0
    Спасибо! работает для меня. Я не могу использовать sqlite, потому что я использую функции, специфичные для mysql (полнотекстовые индексы). Для пользователей Ubuntu вам нужно отредактировать конфигурацию вашего apparmor, чтобы разрешить доступ mysqld к / dev / shm / mysql
  • 0
    Приветствия за головы до Ивана и Потра. На данный момент профиль AppArmor mysql отключен, но нашел руководство по настройке соответствующего локального профиля: blogs.oracle.com/jsmyth/entry/apparmor_and_mysql
Показать ещё 1 комментарий
3

Другой подход: иметь другой экземпляр MySQL, работающий в tempfs, который использует RAM-диск. Инструкции в этом сообщении в блоге: Ускорение MySQL для тестирования в Django.

Преимущества:

  • Вы используете точно такую ​​же базу данных, что ваш производственный сервер использует
  • Не нужно изменять конфигурацию mysql по умолчанию.
2

Расширение ответа 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 используется только через командную строку, поэтому нет необходимости загромождать настройки

  • 0
    Как это позволяет вам работать против вашей производственной базы данных?
  • 0
    потребуется расширить на так - или вы можете установить файл настроек вручную через командную строку - вопрос, который вы проверяете чаще
Показать ещё 10 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню