Як запустити тестову базу даних Django тільки в пам'яті?


125

Мої тести на прилад Django тривають тривалий час, тому я шукаю способи прискорити це. Я розглядаю можливість встановлення SSD , але знаю, що в нього є і свої недоліки. Звичайно, є речі, які я міг би зробити зі своїм кодом, але я шукаю структурне виправлення. Навіть виконання одного тесту відбувається повільно, оскільки базу даних потрібно щоразу відновлювати / мігрувати на південь. Тож ось моя ідея ...

Оскільки я знаю, що база даних тестів завжди буде зовсім невеликою, чому я не можу просто налаштувати систему, щоб завжди зберігати всю тестову базу даних в оперативній пам'яті? Ніколи не торкайтеся диска взагалі. Як налаштувати це в Django? Я вважаю за краще продовжувати використовувати MySQL, оскільки саме це я використовую у виробництві, але якщо SQLite  3 або щось інше полегшує це, я б пішов цим шляхом.

Чи є у SQLite або MySQL можливість повністю запускатися в пам'яті? Потрібно мати можливість налаштувати диск RAM, а потім налаштувати тестову базу даних для зберігання там своїх даних, але я не впевнений, як сказати Django / MySQL використовувати інший каталог даних для певної бази даних, тим більше, що вона видаляється і відтворили кожен пробіг. (Я на Mac FWIW.)

Відповіді:


164

Якщо під час запуску тестів встановити двигун бази даних на 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

9
Так, саме. Я мав би це сказати у своїй відповіді! Поєднайте це з SOUTH_TESTS_MIGRATE = Неправдиво, і ваші тести повинні бути набагато швидшими.
Етьєн

7
це є дивним. для новіших налаштувань django використовують цей рядок: 'ENGINE': 'sqlite3' if 'test' in sys.argv else 'django.db.backends.mysql',
mjallday

3
@Tomasz Zielinski - Хм, це залежить від того, що ви тестуєте. Але я повністю погоджуюся, що наприкінці та час від часу вам потрібно запускати тести зі своєю реальною базою даних (Postgres, MySQL, Oracle ...). Але запуск тестів в пам'яті за допомогою sqlite може заощадити багато часу.
Етьєн

3
Я повертаю -1 до +1: як я це бачу зараз, набагато швидше використовувати sqlite для швидких запусків і перейти на MySQL, наприклад, для фінальних щоденних тестів. (Зауважте, що мені довелося зробити манекенне редагування, щоб розблокувати голосування)
Tomasz Zieliński

12
Обережно з цим "test" in sys.argv; він може спрацьовувати, коли ви цього не хочете, наприклад manage.py collectstatic -i test. sys.argv[1] == "test"є більш точною умовою, яка не повинна мати цю проблему.
keturn

83

Я зазвичай створюю окремий файл налаштувань для тестів і використовую його в тестовій команді, наприклад

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

Він має дві переваги:

  1. Вам не доведеться перевіряти testчи якесь таке магічне слово в sys.argv, 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

    просто щоб бути впевненим, що всі випробування справді проходять.


2
Мені подобається такий підхід. У мене є безліч різних файлів налаштувань і використовую їх для різних середовищ сервера, але я не думав використовувати цей метод для вибору іншої тестової бази даних. Дякую за ідею.
Алексіс Беллідо

Привіт Anurag, я спробував це, але інші мої бази даних, згадані в налаштуваннях, також виконані. Я не в змозі з’ясувати точну причину.
Bhupesh Pant

Гарна відповідь. Цікаво, як вказати файл налаштувань при запуску тестів через покриття.
Wtower

Це хороший підхід, але не СУХА. Джанго вже знає, що ти працюєш на тести. Якби ви могли якось «зачепити» ці знання, ви налаштовані. На жаль, я вважаю, що це вимагає розширення команди управління. Можливо, це має сенс зробити це загальне в ядрі фреймворку, наприклад, встановивши налаштування MANAGEMENT_COMMAND, встановлене для поточної команди, коли виклик Manag.py, або щось для цього.
DylanYoung

2
@DylanYoung ви можете зробити його сухим, включивши основні параметри в тестові настройки та просто перекресливши потрібні для тестування речі.
Anurag Uniyal

22

MySQL підтримує механізм зберігання даних під назвою "MEMORY", який ви можете налаштувати у вашій базі даних config ( 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 за згадку про відсутність підтримки для крапок / стовпців тексту. Він також не підтримує транзакції ( dev.mysql.com/doc/refman/5.6/uk/memory-storage-engine.html ).
Tuukka Mustonen

Якщо ви дійсно хочете тестів на пам'ять, вам, ймовірно, краще працювати з sqlite, який принаймні підтримує транзакції.
atomic77

15

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

По-перше, переконайтеся, що ваша база даних MySQL налаштована для використання InnoDB. Тоді він може використовувати транзакції, щоб відбити стан db перед кожним тестом, що, на мій досвід, призвело до значного прискорення. Ви можете передати команду init бази даних у вашому 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 і база даних буде створена за допомогою простого syncdb, що буде набагато швидше, ніж пробіг через усі історичні міграції.


Чудова порада! Це зменшило мої тести з 369 tests in 498.704sдо 369 tests in 41.334s . Це в 10 разів швидше!
Габі Пуркару

Чи є еквівалентний перемикач у settings.py для міграцій у Django 1.7+?
Едвард Ньюелл

@EdwardNewell Не зовсім. Але ви можете використовувати --keepдля збереження бази даних і не вимагати, щоб ваш повний набір міграцій повторно застосовувався на кожному тестовому запуску. Нові міграції все ще триватимуть. Якщо ви часто перемикаєтесь між гілками, легко перейти в невідповідний стан (ви можете повернути нові міграції перед тим, як перейти, змінивши базу даних на тестову базу даних і запустивши migrate, але це боляче).
DylanYoung

10

Ви можете подвійне налаштування:

  • використовувати транзакційні таблиці: стан початкових світильників встановлюватиметься за допомогою відката бази даних після кожного TestCase.
  • поставте дані вашої бази даних на 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

Обережно, це лише для тестування, після перезавантаження вашої бази даних з пам'яті втрачено!


Дякую! працює для мене. Я не можу використовувати sqlite, оскільки я використовую функції, характерні для mysql (повнотекстові індекси). Для користувачів ubuntu вам доведеться відредагувати конфігурацію apparmor, щоб дозволити доступ mysqld до / dev / shm / mysql
Іван Вірабян

Ура для голови вгору Іван та Потр. Наразі
вимкнено

Хм. Я намагався налаштувати локальний профіль, щоб надати mysqld доступ до шляху / dev / shm / mysql та його вмісту, але служба може запускатися лише в режимі "скаржитися" (команда aa-скаржитися), а не "примушувати" для деяких причина ... Питання для іншого форуму! Я не можу зрозуміти, як взагалі немає жодних скарг, коли це працює, маючи на увазі, що mysqld не порушує профіль ...
trojjer

4

Інший підхід: інший екземпляр MySQL працює в tempfs, який використовує диск RAM. Інструкції в цій публікації в блозі: Прискорення швидкості MySQL для тестування в Django .

Переваги:

  • Ви використовуєте ту саму базу даних, яку використовує ваш виробничий сервер
  • не потрібно змінювати конфігурацію mysql за замовчуванням

2

Розширення відповіді на Anurag я спростив процес, створивши ті ж самі тестові настройки та додавши наступні до management.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 вже імпортований, а Manag.py використовується лише через командний рядок, тому не потрібно переповнювати налаштування


2
Обережно з цим "test" in sys.argv; він може спрацьовувати, коли ви цього не хочете, наприклад manage.py collectstatic -i test. sys.argv[1] == "test"є більш точною умовою, яка не повинна мати цю проблему.
keturn

2
@keturn таким чином створює виняток, коли працює ./manage.pyбез аргументів (наприклад, щоб побачити, які плагіни доступні, як --help)
Ентоні Хетчкінс

1
@AntonyHatchkins Це тривіально для вирішення:len(sys.argv) > 1 and sys.argv[1] == "test"
DylanYoung

1
@DylanYoung Так, саме це я хотів додати Альвіну до свого рішення, але він не особливо зацікавлений у його вдосконаленні. У будь-якому випадку це більше схоже на швидкий злом, ніж на законне рішення.
Ентоні Хеткінс

1
я не переглядав цю відповідь деякий час, я оновив фрагмент, щоб відобразити покращення @ DylanYoung
Елвін

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.