एक db के बिना django इकाई परीक्षण


126

क्या db को स्थापित किए बिना django unittests लिखने की संभावना है? मैं व्यावसायिक तर्क का परीक्षण करना चाहता हूं, जिसे डीबी स्थापित करने की आवश्यकता नहीं है। और जब यह एक db सेटअप करने के लिए तेज़ है, तो मुझे वास्तव में कुछ स्थितियों में इसकी आवश्यकता नहीं है।


मैं सोच रहा था कि क्या वास्तव में मायने रखता है। Db को मेमोरी में रखा जाता है + अगर आपके पास कोई मॉडल नहीं है तो db के साथ कुछ भी नहीं किया जाता है। तो अगर आप की जरूरत नहीं है यह मॉडल स्थापित नहीं है।
टॉरस्टेन एंगलब्रैच

3
मेरे पास मॉडल हैं, लेकिन उन परीक्षणों के लिए वे प्रासंगिक नहीं हैं। और db को मेमोरी में नहीं रखा जाता है, बल्कि mysql में बनाया जाता है, हालाँकि, विशेष रूप से इस उद्देश्य के लिए। ऐसा नहीं है कि मैं यह चाहता हूँ .. हो सकता है कि मैं django को टेस्टिंग के लिए इन-मेमोरी db का उपयोग करने के लिए कॉन्फ़िगर कर सकूं। क्या आप जानते हैं कि यह काम कैसे करना है?
पावेलोके '

मुझे माफ़ करें। जब आप किसी SQLite डेटाबेस का उपयोग करते हैं तो इन-मेमरी डेटाबेस केवल एक मामला होता है। इसके अलावा मुझे टेस्ट डीबी बनाने से बचने का कोई रास्ता नहीं दिख रहा है। डॉक्स में इस बारे में कुछ भी नहीं है + मैंने कभी इससे बचने की जरूरत महसूस नहीं की।
टॉरस्टेन एंगेलब्रेक्ट

3
स्वीकृत उत्तर ने मुझे काम नहीं दिया। इसके बजाय, इसने पूरी तरह से काम किया: caktusgroup.com/blog/2013/10/02/skipping-test-db-creation
ह्यूगो

जवाबों:


122

आप DjangoTestSuiteRunner को subclass कर सकते हैं और पास होने के लिए setup_dat डेटाबेस और teardown_dat डेटाबेस विधियों को ओवरराइड कर सकते हैं।

एक नई सेटिंग फ़ाइल बनाएं और आपके द्वारा अभी बनाई गई नई कक्षा में TEST_RUNNER सेट करें। फिर जब आप अपना परीक्षण चला रहे हों, तो अपनी नई सेटिंग फ़ाइल को-settings ध्वज के साथ निर्दिष्ट करें।

मैंने जो किया था यह रहा:

इसके समान एक कस्टम टेस्ट सूट रनर बनाएँ:

from django.test.simple import DjangoTestSuiteRunner

class NoDbTestRunner(DjangoTestSuiteRunner):
  """ A test runner to test without database creation """

  def setup_databases(self, **kwargs):
    """ Override the database creation defined in parent class """
    pass

  def teardown_databases(self, old_config, **kwargs):
    """ Override the database teardown defined in parent class """
    pass

एक कस्टम सेटिंग बनाएँ:

from mysite.settings import *

# Test runner with no database creation
TEST_RUNNER = 'mysite.scripts.testrunner.NoDbTestRunner'

जब आप अपने परीक्षण चला रहे हों, तो इसे निम्न की तरह चलाएं - अपनी नई सेटिंग फ़ाइल में सेट किए गए झंडे के साथ:

python manage.py test myapp --settings='no_db_settings'

UPDATE: अप्रैल / 2018

Django 1.8 के बाद से, मॉड्यूल को स्थानांतरित कर दिया गया था ।django.test.simple.DjangoTestSuiteRunner 'django.test.runner.DiscoverRunner'

अधिक जानकारी के लिए कस्टम परीक्षण चलाने वालों के बारे में आधिकारिक डॉक्टर अनुभाग देखें।


2
यह त्रुटि तब उठती है जब आपके पास डेटाबेस लेनदेन की आवश्यकता वाले परीक्षण होते हैं। जाहिर है अगर आपके पास डीबी नहीं है, तो आप उन परीक्षणों को चलाने में सक्षम नहीं होंगे। आपको अपने परीक्षण अलग से चलाने चाहिए। यदि आप सिर्फ अजगर प्रबंधन थिंकहोम टेस्ट - settings = new_settings.py का उपयोग करके अपना परीक्षण चलाते हैं, तो यह अन्य एप्लिकेशन से अन्य परीक्षणों का एक पूरा गुच्छा चलाने वाला है, जिन्हें डेटाबेस की आवश्यकता हो सकती है।
मूहि६६

5
ध्यान दें कि आपको अपनी टेस्ट कक्षाओं के लिए टेस्टकेस के बजाय सिंपलटेस्टकेस का विस्तार करना होगा। TestCase एक डेटाबेस की अपेक्षा करता है।
बेन रॉबर्ट्स

9
यदि आप एक नई सेटिंग फ़ाइल का उपयोग नहीं करना चाहते हैं तो आप --testrunnerविकल्प के साथ कमांड लाइन पर नया टेस्टरनर निर्दिष्ट कर सकते हैं ।
चोकर हैंडली

26
बहुत बढ़िया जवाब!! Django 1.8 में, django.test.simple आयात से DjangoTestSuiteRunner को django.test.runner आयात से बदल दिया गया है।
जोश ब्राउन

2
Django 1.8 और इसके बाद के संस्करण में, उपरोक्त कोड में थोड़ा सुधार किया जा सकता है। आयात विवरण को इसमें बदला जा सकता है: django.test.runner import DiscoverRunner से NoDbTestRunner को अब DiscoverRunner वर्ग का विस्तार करना चाहिए।
आदित्य सत्यवाद

77

आम तौर पर एक आवेदन में परीक्षण को दो श्रेणियों में वर्गीकृत किया जा सकता है

  1. यूनिट परीक्षण, ये इनसोल में कोड के अलग-अलग स्निपेट का परीक्षण करते हैं और डेटाबेस में जाने की आवश्यकता नहीं होती है
  2. एकीकरण परीक्षण के मामले जो वास्तव में डेटाबेस में जाते हैं और पूरी तरह से एकीकृत तर्क का परीक्षण करते हैं।

Django इकाई और एकीकरण परीक्षण दोनों का समर्थन करता है।

यूनिट परीक्षण, डेटाबेस को सेटअप और आंसू करने की आवश्यकता नहीं होती है और हमें SimpleTestCase से इनहेरिट करना चाहिए ।

from django.test import SimpleTestCase


class ExampleUnitTest(SimpleTestCase):
    def test_something_works(self):
        self.assertTrue(True)

एकीकरण परीक्षण मामलों के लिए TransCaseTestCase से बदले में TestCase से विरासत में मिला है और यह प्रत्येक परीक्षण को चलाने से पहले डेटाबेस को सेटअप और फाड़ देगा।

from django.test import TestCase


class ExampleIntegrationTest(TestCase):
    def test_something_works(self):
        #do something with database
        self.assertTrue(True)

यह रणनीति सुनिश्चित करेगी कि डेटाबेस केवल उन परीक्षण मामलों के लिए बनाया और नष्ट किया जाए जो डेटाबेस तक पहुंचते हैं और इसलिए परीक्षण अधिक कुशल होंगे


37
यह रनिंग टेस्ट को अधिक कुशल बना सकता है, लेकिन ध्यान दें कि टेस्ट रनर अभी भी इनिशियलाइज़ेशन पर टेस्ट डेटाबेस बनाता है।
मोनकुट

6
इतना सरल कि चुना हुआ उत्तर। आपको बहुत - बहुत धन्यवाद!
केफंक

1
@monkut नहीं ... यदि u के पास केवल SimpleTestCase क्लास है, तो टेस्ट रनर कुछ भी नहीं चलाता है, इस प्रोजेक्ट को देखें ।
क्लाउडियो सैंटोस

Django अभी भी एक परीक्षण DB बनाने की कोशिश करेगा भले ही आप केवल SimpleTestCase का उपयोग करें। इस प्रश्न को देखें ।
Marko Prcać

SimpleTestCase का उपयोग करना उपयोगिता विधियों या स्निपेट के परीक्षण के लिए बिल्कुल काम करता है और परीक्षण db का उपयोग या निर्माण नहीं करता है। बिल्कुल मुझे क्या चाहिए!
टायरो हंटर

28

से django.test.simple

  warnings.warn(
      "The django.test.simple module and DjangoTestSuiteRunner are deprecated; "
      "use django.test.runner.DiscoverRunner instead.",
      RemovedInDjango18Warning)

इसलिए DiscoverRunnerइसके बजाय ओवरराइड करें DjangoTestSuiteRunner

 from django.test.runner import DiscoverRunner

 class NoDbTestRunner(DiscoverRunner):
   """ A test runner to test without database creation/deletion """

   def setup_databases(self, **kwargs):
     pass

   def teardown_databases(self, old_config, **kwargs):
     pass

उस तरह का उपयोग करें:

python manage.py test app --testrunner=app.filename.NoDbTestRunner

8

मैंने पद्धति से विरासत django.test.runner.DiscoverRunnerमें कुछ जोड़े और बनाने का विकल्प चुना run_tests

यह देखने के लिए मेरा पहला अतिरिक्त जाँच है कि क्या db सेट करना आवश्यक है और setup_databasesयदि db आवश्यक है तो सामान्य कार्यक्षमता को किक करने की अनुमति देता है। मेरा दूसरा जोड़ सामान्य teardown_databasesचलाने की अनुमति देता है यदि setup_databasesविधि को चलाने की अनुमति दी गई थी।

मेरा कोड मानता है कि किसी भी TestCase से विरासत में मिला django.test.TransactionTestCase(और इस तरह django.test.TestCase) सेटअप करने के लिए डेटाबेस की आवश्यकता होती है। मैंने यह अनुमान लगाया क्योंकि Django डॉक्स का कहना है:

यदि आपको किसी अन्य अधिक जटिल और हैवीवेट Django- विशिष्ट सुविधाओं की आवश्यकता है ... जैसे ORM का परीक्षण करना या उपयोग करना ... तो आपको इसके बजाय TransactionTestCase या TestCase का उपयोग करना चाहिए।

https://docs.djangoproject.com/en/1.6/topics/testing/tools/#django.test.SimpleTestCase

mysite / scripts / settings.py

from django.test import TransactionTestCase     
from django.test.runner import DiscoverRunner


class MyDiscoverRunner(DiscoverRunner):
    def run_tests(self, test_labels, extra_tests=None, **kwargs):
        """
        Run the unit tests for all the test labels in the provided list.

        Test labels should be dotted Python paths to test modules, test
        classes, or test methods.

        A list of 'extra' tests may also be provided; these tests
        will be added to the test suite.

        If any of the tests in the test suite inherit from
        ``django.test.TransactionTestCase``, databases will be setup. 
        Otherwise, databases will not be set up.

        Returns the number of tests that failed.
        """
        self.setup_test_environment()
        suite = self.build_suite(test_labels, extra_tests)
        # ----------------- First Addition --------------
        need_databases = any(isinstance(test_case, TransactionTestCase) 
                             for test_case in suite)
        old_config = None
        if need_databases:
        # --------------- End First Addition ------------
            old_config = self.setup_databases()
        result = self.run_suite(suite)
        # ----------------- Second Addition -------------
        if need_databases:
        # --------------- End Second Addition -----------
            self.teardown_databases(old_config)
        self.teardown_test_environment()
        return self.suite_result(suite, result)

अंत में, मैंने अपने प्रोजेक्ट की सेटिंग्स ओडीएफ फ़ाइल में निम्न पंक्ति जोड़ी।

mysite / settings.py

TEST_RUNNER = 'mysite.scripts.settings.MyDiscoverRunner'

अब, जब केवल गैर-डीबी-आश्रित परीक्षण चल रहा है, तो मेरा परीक्षण सूट तेजी से परिमाण का क्रम चलाता है! :)


6

अपडेट किया गया: तृतीय-पक्ष टूल का उपयोग करने के लिए इस उत्तर को भी देखें pytest


@ सीजर सही है। गलती से चलने के बाद ./manage.py test --settings=no_db_settings, एक ऐप नाम निर्दिष्ट किए बिना, मेरे विकास डेटाबेस को मिटा दिया गया था।

सुरक्षित तरीके से, उसी का उपयोग करें NoDbTestRunner, लेकिन निम्नलिखित के साथ संयोजन में mysite/no_db_settings.py:

from mysite.settings import *

# Test runner with no database creation
TEST_RUNNER = 'mysite.scripts.testrunner.NoDbTestRunner'

# Use an alternative database as a safeguard against accidents
DATABASES['default']['NAME'] = '_test_mysite_db'

आपको _test_mysite_dbएक बाहरी डेटाबेस टूल का उपयोग करके एक डेटाबेस बनाने की आवश्यकता है । फिर संबंधित तालिकाओं को बनाने के लिए निम्न कमांड चलाएँ:

./manage.py syncdb --settings=mysite.no_db_settings

यदि आप दक्षिण का उपयोग कर रहे हैं, तो निम्न कमांड भी चलाएं:

./manage.py migrate --settings=mysite.no_db_settings

ठीक है!

अब आप इकाई परीक्षणों को तेज गति से (और सुरक्षित) चला सकते हैं:

./manage.py test myapp --settings=mysite.no_db_settings

मैंने pytest (pytest-django plugin के साथ) और NoDbTestRunner का उपयोग करके परीक्षण चलाए हैं, अगर किसी तरह से आप किसी टेस्टकेस में दुर्घटना से कोई ऑब्जेक्ट बनाते हैं और आप डेटाबेस के नाम को ओवरराइड नहीं करते हैं, तो ऑब्जेक्ट आपके स्थानीय डेटाबेस में बनाया जाएगा जिसे आप सेटअप करते हैं समायोजन। 'NoDbTestRunner' नाम 'NoTestDbTestRunner' होना चाहिए क्योंकि यह परीक्षण डेटाबेस नहीं बनाएगा, लेकिन सेटिंग्स से आपके डेटाबेस का उपयोग करेगा।
बजे गेब्रियल मुज

2

NoDbTestRunner को "सुरक्षित" बनाने के लिए अपनी सेटिंग्स को संशोधित करने के विकल्प के रूप में, यहां NoDbTestRunner का एक संशोधित संस्करण है जो वर्तमान डेटाबेस कनेक्शन को बंद कर देता है और सेटिंग्स और कनेक्शन ऑब्जेक्ट से कनेक्शन की जानकारी को हटा देता है। मेरे लिए काम करता है, इस पर भरोसा करने से पहले अपने वातावरण में इसका परीक्षण करें :)

class NoDbTestRunner(DjangoTestSuiteRunner):
    """ A test runner to test without database creation """

    def __init__(self, *args, **kwargs):
        # hide/disconnect databases to prevent tests that 
        # *do* require a database which accidentally get 
        # run from altering your data
        from django.db import connections
        from django.conf import settings
        connections.databases = settings.DATABASES = {}
        connections._connections['default'].close()
        del connections._connections['default']
        super(NoDbTestRunner,self).__init__(*args,**kwargs)

    def setup_databases(self, **kwargs):
        """ Override the database creation defined in parent class """
        pass

    def teardown_databases(self, old_config, **kwargs):
        """ Override the database teardown defined in parent class """
        pass

नोट: यदि आप कनेक्शन सूची से डिफ़ॉल्ट कनेक्शन को हटाते हैं तो आप Django मॉडल या अन्य सुविधाओं का उपयोग नहीं कर पाएंगे जो सामान्य रूप से डेटाबेस का उपयोग करते हैं (जाहिर है कि हम डेटाबेस के साथ संवाद नहीं करते हैं लेकिन Django विभिन्न विशेषताओं की जांच करता है जो DB का समर्थन करता है) । यह भी लगता है कि कनेक्शन._ कनेक्शन __getitem__अब समर्थन नहीं करते हैं। उपयोग connections._connections.defaultवस्तु तक पहुँचने के लिए।
The_drow

2

एक अन्य समाधान यह होगा कि आपके परीक्षण वर्ग को केवल unittest.TestCaseDjango के परीक्षण वर्गों में से किसी से विरासत में मिला । Django डॉक्स ( https://docs.djangoproject.com/en/2.0/topics/testing/overview/#writing-tests ) इस बारे में निम्नलिखित चेतावनी देते हैं:

Unittest.TestCase का उपयोग लेनदेन में प्रत्येक परीक्षण को चलाने और डेटाबेस को फ्लश करने से बचाता है, लेकिन यदि आपके परीक्षण डेटाबेस के साथ बातचीत करते हैं, तो उनका व्यवहार उस क्रम के आधार पर अलग-अलग होगा जो परीक्षण धावक उन्हें निष्पादित करता है। इससे यूनिट परीक्षण हो सकते हैं जो अलगाव में चलने पर एक सूट में चलने पर विफल हो जाते हैं।

हालाँकि, यदि आपका परीक्षण डेटाबेस का उपयोग नहीं करता है, तो इस चेतावनी की आपको चिंता नहीं है और आप प्रत्येक परीक्षण मामले को लेनदेन में नहीं चलाने के लाभों को प्राप्त कर सकते हैं।


ऐसा लगता है कि यह अभी भी डीबी बनाता है और नष्ट कर देता है, केवल अंतर यह है कि यह एक लेनदेन में परीक्षण नहीं चलाता है और डीबी को फ्लश नहीं करता है।
कैम रेल

0

उपरोक्त उपाय भी ठीक हैं। लेकिन निम्नलिखित समाधान भी db निर्माण समय को कम कर देगा अगर अधिक संख्या में पलायन होते हैं। यूनिट टेस्टिंग के दौरान, सभी साउथ माइग्रेशन को चलाने के बजाय सिंकबैंक चलाना ज्यादा तेज होगा।

SOUTH_TESTS_MIGRATE = गलत माइग्रेशन को अक्षम करने के लिए और इसके बजाय सिंकबब का उपयोग करें


0

मेरा वेब होस्ट केवल अपने वेब GUI से डेटाबेस बनाने और छोड़ने की अनुमति देता है, इसलिए मुझे परीक्षण डेटाबेस बनाने में "त्रुटि मिली: अनुमति अस्वीकार" त्रुटि जब चलाने की कोशिश कर रहा था python manage.py test

मैं django-admin.py के लिए --keepdb विकल्प का उपयोग करने की आशा करता हूं, लेकिन यह DjNO 1.7 के रूप में अब समर्थित नहीं लगता है।

मैंने जो कुछ किया वह Django कोड को संशोधित कर रहा था ... / django / db / backends / creation.py, विशेष रूप से _create_test_db और _destroy_test_db फ़ंक्शन।

के लिए _create_test_dbमैं cursor.execute("CREATE DATABASE ...लाइन बाहर टिप्पणी की है और इसे ब्लॉक passतो tryखाली नहीं होगा के साथ बदल दिया ।

क्योंकि _destroy_test_dbमैंने अभी टिप्पणी की थी cursor.execute("DROP DATABASE- मुझे इसे किसी भी चीज़ से बदलने की आवश्यकता नहीं थी क्योंकि ब्लॉक में पहले से ही एक और कमांड थी ( time.sleep(1))।

उसके बाद मेरे परीक्षण ठीक चले - हालांकि मैंने अपने नियमित डेटाबेस के अलग से एक test_ संस्करण स्थापित किया था।

यह निश्चित रूप से एक महान समाधान नहीं है, क्योंकि यह टूट जाएगा अगर Django अपग्रेड किया गया है, लेकिन मेरे पास virtualenv का उपयोग करने के कारण Django की एक स्थानीय प्रतिलिपि थी इसलिए कम से कम मेरा नियंत्रण है जब मैं एक नए संस्करण में अपग्रेड करता / करती हूं।


0

एक अन्य समाधान का उल्लेख नहीं किया गया है: यह मेरे लिए लागू करना आसान था क्योंकि मेरे पास पहले से ही कई सेटिंग्स फाइलें हैं (स्थानीय / मंचन / उत्पादन के लिए) जो बेसहोम से विरासत में मिली हैं। इसलिए अन्य लोगों के विपरीत मुझे DATABASES ['डिफ़ॉल्ट'] को अधिलेखित करने की आवश्यकता नहीं थी, क्योंकि DATABASES आधारभूत में सेट नहीं है

SimpleTestCase ने अभी भी मेरे परीक्षण डेटाबेस से कनेक्ट करने और माइग्रेशन चलाने की कोशिश की। जब मैंने एक config / settings / test.py फ़ाइल बनाई जिसमें कुछ भी DATABASES सेट नहीं किया था, तो मेरी इकाई परीक्षण इसके बिना चले। इसने मुझे उन मॉडलों का उपयोग करने की अनुमति दी जिनके पास विदेशी कुंजी और अद्वितीय बाधा क्षेत्र थे। (विदेशी कुंजी लुकअप को उल्टा करें, जिसमें db लुकअप की आवश्यकता होती है, विफल रहता है।)

(Django 2.0.6)

पुनश्च कोड स्निपेट

PROJECT_ROOT_DIR/config/settings/test.py:
from .base import *
#other test settings

#DATABASES = {
# 'default': {
#   'ENGINE': 'django.db.backends.sqlite3',
#   'NAME': 'PROJECT_ROOT_DIR/db.sqlite3',
# }
#}

cli, run from PROJECT_ROOT_DIR:
./manage.py test path.to.app.test --settings config.settings.test

path/to/app/test.py:
from django.test import SimpleTestCase
from .models import *
#^assume models.py imports User and defines Classified and UpgradePrice

class TestCaseWorkingTest(SimpleTestCase):
  def test_case_working(self):
    self.assertTrue(True)
  def test_models_ok(self):
    obj = UpgradePrice(title='test',price=1.00)
    self.assertEqual(obj.title,'test')
  def test_more_complex_model(self):
    user = User(username='testuser',email='hi@hey.com')
    self.assertEqual(user.username,'testuser')
  def test_foreign_key(self):
    user = User(username='testuser',email='hi@hey.com')
    ad = Classified(user=user,headline='headline',body='body')
    self.assertEqual(ad.user.username,'testuser')
  #fails with error:
  def test_reverse_foreign_key(self):
    user = User(username='testuser',email='hi@hey.com')
    ad = Classified(user=user,headline='headline',body='body')
    print(user.classified_set.first())
    self.assertTrue(True) #throws exception and never gets here

0

नाक परीक्षण धावक (django- नाक) का उपयोग करते समय, आप कुछ इस तरह कर सकते हैं:

my_project/lib/nodb_test_runner.py:

from django_nose import NoseTestSuiteRunner


class NoDbTestRunner(NoseTestSuiteRunner):
    """
    A test runner to test without database creation/deletion
    Used for integration tests
    """
    def setup_databases(self, **kwargs):
        pass

    def teardown_databases(self, old_config, **kwargs):
        pass

अपने में settings.pyआप परीक्षण धावक को निर्दिष्ट कर सकते हैं, अर्थात

TEST_RUNNER = 'lib.nodb_test_runner.NoDbTestRunner' . # Was 'django_nose.NoseTestSuiteRunner'

या

मैं इसे केवल विशिष्ट परीक्षण चलाने के लिए चाहता था, इसलिए मैं इसे इस तरह चलाता हूं:

python manage.py test integration_tests/integration_*  --noinput --testrunner=lib.nodb_test_runner.NoDbTestRunner
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.