Django의 테스트 데이터베이스를 메모리에서만 실행하는 방법은 무엇입니까?


125

내 Django 단위 테스트는 실행하는 데 오랜 시간이 걸리므로 속도를 높이는 방법을 찾고 있습니다. SSD 설치를 고려하고 있지만 단점도 있다는 것을 알고 있습니다. 물론 내 코드로 할 수있는 일이 있지만 구조적 수정을 찾고 있습니다. 매번 데이터베이스를 다시 빌드 / 마이그레이션해야하기 때문에 단일 테스트를 실행하는 것조차 느립니다. 그래서 여기 내 생각이 있습니다.

테스트 데이터베이스가 항상 매우 작다는 것을 알고 있기 때문에 전체 테스트 데이터베이스를 항상 RAM에 유지하도록 시스템을 구성 할 수없는 이유는 무엇입니까? 디스크를 절대 만지지 마십시오. 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 마이그레이션에 문제가있는 경우 다음 줄을 추가 할 수도 있습니다.

    SOUTH_TESTS_MIGRATE = False

9
네, 맞습니다. 내 대답에 넣어야 했어! SOUTH_TESTS_MIGRATE = False와 결합하면 테스트가 훨씬 빨라집니다.
Etienne

7
이것은 이다 멋진. 최신 django 설정에서는 다음 줄을 사용합니다. 'ENGINE': 'sqlite3'if 'test'in sys.argv else 'django.db.backends.mysql',
mjallday

3
@Tomasz Zielinski-흠, 테스트 대상에 따라 다릅니다. 하지만 마지막에 때때로 실제 데이터베이스 (Postgres, MySQL, Oracle ...)로 테스트를 실행해야한다는 데 전적으로 동의합니다. 그러나 sqlite를 사용하여 메모리에서 테스트를 실행하면 많은 시간을 절약 할 수 있습니다.
Etienne

3
나는 -1을 +1로 역전시킨다. 지금 보듯이 빠른 실행을 위해 sqlite를 사용하고 예를 들어 최종 일일 테스트를 위해 MySQL로 전환하는 것이 훨씬 빠릅니다. (투표를 잠금 해제하려면 더미 편집을해야했습니다.)
Tomasz Zieliński 2011-06-25

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. testsys.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
나는이 접근 방식을 좋아합니다. 다양한 설정 파일이 있고 다른 서버 환경에 사용하지만 다른 테스트 데이터베이스를 선택하는 데이 방법을 사용할 생각은 없었습니다. 아이디어 주셔서 감사합니다.
Alexis Bellido 2013

안녕하세요 Anurag, 나는 이것을 시도했지만 설정에 언급 된 다른 데이터베이스도 실행됩니다. 정확한 이유를 알 수 없습니다.
Bhupesh Pant 2014-06-11

좋은 대답입니다. 커버리지를 통해 테스트를 실행할 때 설정 파일을 지정하는 방법이 궁금합니다.
Wtower

좋은 접근 방식이지만 DRY는 아닙니다. Django는 이미 테스트를 실행하고 있다는 것을 알고 있습니다. 어떻게 든이 지식에 '연결'할 수 있다면 설정 될 것입니다. 안타깝게도 관리 명령을 확장해야한다고 생각합니다. 예를 들어 manage.py가 호출 될 때마다 MANAGEMENT_COMMAND를 현재 명령으로 설정하거나 그 효과를내는 것과 같이 프레임 워크의 핵심에서이 일반화를 만드는 것이 합리적 일 것입니다.
DylanYoung dec.

2
@DylanYoung은 기본 설정을 test_settings에 포함하고 테스트를 위해 원하는 것을 재정의함으로써 건조하게 만들 수 있습니다.
Anurag Uniyal

22

MySQL은 데이터베이스 구성 ( settings.py)에서 다음과 같이 구성 할 수있는 "MEMORY"라는 스토리지 엔진을 지원합니다 .

    '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
Blob / 텍스트 열에 대한 지원 부족을 언급 한 경우 +1. 또한 트랜잭션을 지원하지 않는 것 같습니다 ( dev.mysql.com/doc/refman/5.6/en/memory-storage-engine.html ).
Tuukka Mustonen 2011 년

정말로 인 메모리 테스트를 원한다면 적어도 트랜잭션을 지원하는 sqlite를 사용하는 것이 좋습니다.
atomic77 2018 년

15

주요 질문에 답할 수는 없지만 작업 속도를 높이기 위해 할 수있는 몇 가지가 있습니다.

먼저 MySQL 데이터베이스가 InnoDB를 사용하도록 설정되어 있는지 확인하십시오. 그런 다음 트랜잭션을 사용하여 각 테스트 전에 db의 상태를 롤백 할 수 있는데, 내 경험상 엄청난 속도 향상을 가져 왔습니다. settings.py (Django 1.2 구문)에서 데이터베이스 init 명령을 전달할 수 있습니다.

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

둘째, 매번 South 마이그레이션을 실행할 필요가 없습니다. 설정 SOUTH_TESTS_MIGRATE = False당신의 settings.py에서 데이터베이스는 더 빨리 모든 역사적인 마이그레이션을 통해 실행하는 것보다 될 것입니다 일반 syncdb로 생성됩니다.


좋은 팁! 내 테스트를 369 tests in 498.704s에서 369 tests in 41.334s . 이것은 10 배 이상 빠릅니다!
Gabi Purcaru

Django 1.7 이상에서 마이그레이션을 위해 settings.py에 동등한 스위치가 있습니까?
Edward Newell 2015 년

@EdwardNewell 정확히는 아닙니다. 그러나 --keep데이터베이스를 유지하는 데 사용할 수 있으며 모든 테스트 실행에 전체 마이그레이션 집합을 다시 적용 할 필요는 없습니다. 새 마이그레이션은 계속 실행됩니다. 브랜치 사이를 자주 전환하는 경우 일관성없는 상태가되기 쉽습니다 (데이터베이스를 테스트 데이터베이스로 변경하고를 실행하여 전환하기 전에 새 마이그레이션을 되돌릴 수 migrate있지만 약간 고통 스럽습니다).
DylanYoung dec.

10

이중 조정을 수행 할 수 있습니다.

  • 트랜잭션 테이블 사용 : 초기 조명기 상태는 모든 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

메모리에서 데이터베이스를 다시 부팅 한 후에는 테스트 용일뿐입니다.


감사! 나를 위해 작동합니다. mysql (전체 텍스트 인덱스)에 특정한 기능을 사용하고 있기 때문에 sqlite를 사용할 수 없습니다. 우분투 사용자의 경우 mysqld에 액세스 할 수 있도록 편집에 AppArmor의의 설정을해야합니다으로는 / dev / SHM / mysql을
이반 Virabyan

Ivan과 Potr의 머리를 응원합니다. 지금은 AppArmor mysql 프로필을 비활성화했지만 관련 로컬 프로필을 사용자 지정하기위한 가이드를 찾았습니다. blogs.oracle.com/jsmyth/entry/apparmor_and_mysql
trojjer

흠. mysqld에 / dev / shm / mysql 경로 및 해당 내용에 대한 액세스 권한을 부여하기 위해 로컬 프로필을 사용자 지정하려고 시도했지만 일부 서비스의 경우 'complain'모드 (aa-complain 명령)로만 시작할 수 있고 'enforce'가 아닙니다. 이유 ... 다른 포럼에 대한 질문! 내가 이해할 수없는 것은 전혀 '불만'에는이 얼마나 없을 때 수행 작업, 그 mysqld를 암시하는 프로파일을 위반하지 않습니다 ...
trojjer

4

또 다른 접근 방식 : RAM 디스크를 사용하는 tempfs에서 MySQL의 다른 인스턴스를 실행하는 것입니다. 이 블로그 게시물의 지침 : Django 테스트를 위해 MySQL 속도 향상 .

장점 :

  • 프로덕션 서버에서 사용하는 것과 똑같은 데이터베이스를 사용합니다.
  • 기본 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는 명령 줄을 통해서만 사용되므로 더 깔끔해 보입니다. 설정을 복잡하게 만들 필요가 없습니다.


2
이것에 대한주의 "test" in sys.argv; 원하지 않을 때 트리거 될 수 있습니다 (예 :) manage.py collectstatic -i test. sys.argv[1] == "test"그 문제가 없어야하는보다 정확한 상태입니다.
keturn

2
실행될 때 예외를 발생 @keturn이 방법 ./manage.py(예를 들면 동일하게 사용할 수있는 플러그인 볼 인수없이 --help)
안토니 Hatchkins을

1
사소한 @AntonyHatchkins를 해결하기 위해 :len(sys.argv) > 1 and sys.argv[1] == "test"
DylanYoung

1
@DylanYoung 예, Alvin이 그의 솔루션에 추가하기를 원했지만 그는 특별히 개선하는 데 관심이 없습니다. 어쨌든 합법적 인 솔루션보다 빠른 해킹처럼 보입니다.
Antony Hatchkins

1
잠시 동안이 답변을 보지 않았으므로 @DylanYoung의 개선 사항을 반영하기 위해 스 니펫을 업데이트했습니다.
Alvin

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.