จะเรียกใช้ฐานข้อมูลทดสอบของ 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_TESTS_MIGRATE = False

9
ใช่แน่นอน ฉันควรจะใส่คำตอบนั้น! รวมเข้ากับ SOUTH_TESTS_MIGRATE = False และการทดสอบของคุณจะเร็วขึ้นมาก
Etienne

7
นี้เป็นที่น่ากลัว ในการตั้งค่า django ที่ใหม่กว่าให้ใช้บรรทัดนี้: 'ENGINE': 'sqlite3' if 'test' ใน sys.argv else 'django.db.backends.mysql',
mjallday

3
@Tomasz Zielinski - อืมมันขึ้นอยู่กับสิ่งที่คุณกำลังทดสอบ แต่ฉันยอมรับโดยสิ้นเชิงว่าในตอนท้ายและในบางครั้งคุณต้องเรียกใช้การทดสอบกับฐานข้อมูลจริงของคุณ (Postgres, MySQL, Oracle ... ) แต่การเรียกใช้การทดสอบในหน่วยความจำของคุณด้วย sqlite สามารถช่วยคุณประหยัดเวลาได้มาก
Etienne

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
ฉันชอบแนวทางนี้ ฉันมีไฟล์การตั้งค่าที่แตกต่างกันมากมายและใช้กับสภาพแวดล้อมเซิร์ฟเวอร์ที่แตกต่างกัน แต่ฉันไม่ได้คิดที่จะใช้วิธีนี้เพื่อเลือกฐานข้อมูลทดสอบอื่น ขอบคุณสำหรับความคิด
Alexis Bellido

สวัสดี Anurag ฉันได้ลองสิ่งนี้ แต่ฐานข้อมูลอื่น ๆ ของฉันที่กล่าวถึงในการตั้งค่าก็ถูกเรียกใช้เช่นกัน ฉันไม่สามารถหาเหตุผลที่แน่นอนได้
Bhupesh Pant

คำตอบที่ดี ฉันสงสัยว่าจะระบุไฟล์การตั้งค่าได้อย่างไรเมื่อเรียกใช้การทดสอบผ่านความครอบคลุม
Wtower

เป็นแนวทางที่ดี แต่ไม่แห้ง Django รู้แล้วว่าคุณกำลังทำการทดสอบ หากคุณสามารถ 'เชื่อมโยง' ความรู้นี้ได้คุณก็จะถูกตั้งค่า น่าเสียดายที่ฉันเชื่อว่าต้องขยายคำสั่งการจัดการ มันอาจจะสมเหตุสมผลที่จะทำให้สิ่งนี้เป็นแบบทั่วไปในแกนหลักของเฟรมเวิร์กโดยตัวอย่างเช่นมีการตั้งค่า MANAGEMENT_COMMAND ที่ตั้งค่าเป็นคำสั่งปัจจุบันเมื่อใดก็ตามที่มีการเรียก Manage.py หรือบางสิ่งบางอย่างที่เป็นเอฟเฟกต์นั้น
DylanYoung

2
@DylanYoung คุณสามารถทำให้แห้งได้โดยรวมการตั้งค่าหลักลงใน test_settings และเพียงแค่แทนที่สิ่งที่คุณต้องการสำหรับการทดสอบ
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/en/memory-storage-engine.html )
Tuukka Mustonen

หากคุณต้องการทดสอบในหน่วยความจำจริงๆคุณน่าจะดีกว่าที่จะใช้ sqlite ซึ่งอย่างน้อยก็รองรับธุรกรรม
atomic77

15

ฉันไม่สามารถตอบคำถามหลักของคุณได้ แต่มีสองสิ่งที่คุณสามารถทำได้เพื่อเร่งความเร็ว

ประการแรกตรวจสอบให้แน่ใจว่าฐานข้อมูล MySQL ของคุณได้รับการตั้งค่าให้ใช้ InnoDB จากนั้นสามารถใช้ธุรกรรมเพื่อย้อนกลับสถานะของฐานข้อมูลก่อนการทดสอบแต่ละครั้งซึ่งจากประสบการณ์ของฉันได้นำไปสู่การเพิ่มความเร็วอย่างมาก คุณสามารถส่งคำสั่ง 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 เท่า!
Gabi Purcaru

มีสวิตช์ที่เทียบเท่าใน settings.py สำหรับการย้ายข้อมูลใน Django 1.7+ หรือไม่
Edward Newell

@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 config ของคุณเพื่ออนุญาตให้ mysqld เข้าถึง / dev / shm / mysql
Ivan Virabyan

ไชโยสำหรับหัวของ Ivan และ Potr ปิดใช้งานโปรไฟล์ AppArmor mysql ในตอนนี้ แต่พบคำแนะนำสำหรับการปรับแต่งโปรไฟล์ภายในที่เกี่ยวข้อง: blogs.oracle.com/jsmyth/entry/apparmor_and_mysql
trojjer

อืมมม ฉันได้ลองปรับแต่งโปรไฟล์โลคัลเพื่อให้ mysqld เข้าถึงพา ธ / dev / shm / mysql และเนื้อหาของมัน แต่บริการสามารถเริ่มได้ในโหมด 'บ่น' เท่านั้น (คำสั่ง aa-บ่น) และไม่ 'บังคับ' สำหรับบางคน เหตุผล ... คำถามสำหรับเว็บบอร์ดอื่น! สิ่งที่ฉันไม่เข้าใจคือไม่มี 'ข้อร้องเรียน' ใด ๆ เลยเมื่อมันใช้งานได้หมายความว่า mysqld ไม่ได้ละเมิดโปรไฟล์ ...
trojjer

4

อีกวิธีหนึ่ง: มี MySQL อินสแตนซ์อื่นที่ทำงานใน tempfs ที่ใช้ RAM Disk คำแนะนำในการโพสต์บล็อกนี้: เร่ง 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 ใช้ผ่านบรรทัดคำสั่งเท่านั้นดังนั้นจึงไม่จำเป็นต้องถ่วงการตั้งค่า


2
ข้อควรระวังนี้"test" in sys.argv; manage.py collectstatic -i testมันอาจทำให้เกิดเมื่อคุณไม่ต้องการให้เช่น sys.argv[1] == "test"เป็นเงื่อนไขที่ชัดเจนยิ่งขึ้นซึ่งไม่ควรมีปัญหานั้น
keturn

2
@keturn ด้วยวิธีนี้จะสร้างข้อยกเว้นเมื่อทำงาน./manage.pyโดยไม่มีข้อโต้แย้ง (เช่นเพื่อดูว่ามีปลั๊กอินใดบ้างเช่นเดียวกับ--help)
Antony 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.