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.