2025, Oct 18 19:31

pytest-django के साथ Django टेस्ट डेटाबेस को सुरक्षित रूप से अलग करें (VS Code)

VS Code में pytest-django चलाते समय Django के टेस्ट डेटाबेस आइसोलेशन को कैसे बहाल करें: सक्रिय कनेक्शन जांचें, conftest.py ओवरराइड हटाएँ और सुरक्षित टेस्ट DB सेटअप अपनाएँ.

Django की टेस्ट सूट को अलग-थलग डेटाबेस पर चलना चाहिए, न कि आपके लोकल वर्किंग डेटाबेस पर। फिर भी, IDE से pytest, pytest-django और कस्टम टेस्ट रनर को जोड़ते समय अनजाने में प्राइमरी डेटाबेस पर ही क्वेरी चल जाना हैरानी से आसान है। नीचे संक्षेप में बताया गया है कि गड़बड़ी कहाँ होती है, यह कैसे जाँचें कि आपके टेस्ट सच में किस डेटाबेस का उपयोग कर रहे हैं, और वह न्यूनतम कॉन्फ़िगरेशन जो Django के डिफ़ॉल्ट, सुरक्षित व्यवहार को वापस लाता है।

लक्षण

टेस्ट Django 3.2.25 प्रोजेक्ट में pytest 8.2.2 के साथ VS Code के टेस्ट रनर से चलाए जा रहे हैं। उम्मीद यह रहती है कि रन के लिए एक अस्थायी, फेंक देने योग्य डेटाबेस डायनेमिक रूप से बनेगा। लेकिन टेस्ट कोड दिखाता है कि कनेक्शन सामान्य डेटाबेस नाम की ओर ही इशारा कर रहा है।

समस्या का प्रदर्शन

डेटाबेस कॉन्फ़िगरेशन में TEST सेक्शन स्पष्ट रूप से दिया गया है, और टेस्ट एक्टिव डेटाबेस का नाम प्रिंट करता है:

DATABASES = {
    'default': {
        "NAME": "localDatabase",
        "ENGINE": "django.contrib.gis.db.backends.postgis",
        "USER": "test",
        "PASSWORD": "test",
        "HOST": "127.0.0.1",
        "PORT": "5432",
        "TEST": {
            "NAME": "test_local"
        },
    },
}
from rest_framework.test import APITestCase
from rest_framework import status
from django.db import connection
class InvoiceApiSpec(APITestCase):
    def test_can_create_invoice(self):
        print(connection.settings_dict["NAME"])  # यहाँ टेस्ट DB का नाम होना चाहिए
        resp = self.client.post(self.endpoint, self.sample_payload, format="json")
        self.assertEqual(resp.status_code, status.HTTP_405_METHOD_NOT_ALLOWED)

प्रिंट का आउटपुट इच्छित टेस्ट डेटाबेस नाम की बजाय localDatabase आता है।

असल में हो क्या रहा है

Django डिफ़ॉल्ट रूप से टेस्ट चलाते समय कॉन्फ़िगर किए गए डेटाबेस नाम के आगे test_ जोड़कर एक अलग टेस्ट डेटाबेस बनाता है। यही बिल्ट-इन व्यवहार है। लेकिन conftest.py में मौजूद एक पुराना pytest फ़िक्स्चर Django के डिफ़ॉल्ट टेस्ट डेटाबेस निर्माण प्रवाह को ओवरराइड कर रहा था। नतीजा यह हुआ कि टेस्ट रनर ने अस्थायी डेटाबेस प्रोविजन ही नहीं किया और मूल डेटाबेस सेटिंग्स को ज्यों का त्यों इस्तेमाल करता रहा।

यह जाँचने के लिए कि टेस्ट के दौरान अभी कौन-सा डेटाबेस उपयोग में है, कनेक्शन का नाम निकालें:

from django.db import connection
connection.settings_dict["NAME"]

यह रनटाइम पर वही वास्तविक डेटाबेस प्रिंट करता है जिससे आपका टेस्ट कनेक्शन जुड़ा हुआ है—सिर्फ settings.py में क्या लिखा है, इतना भर नहीं।

उपाय

टेस्ट डेटाबेस निर्माण में दखल देने वाले ओवरराइड्स हटाएँ और DATABASES["default"]["TEST"] के जरिए टेस्ट का NAME जबरन सेट करने से बचें। जब ओवरराइड और TEST कुंजी दोनों हटा दी जाती हैं, Django टेस्ट रन की अवधि के लिए अपने आप test_<original_name> नाम का डेटाबेस बना देता है।

काम करने वाला कॉन्फ़िगरेशन कुछ ऐसा दिखता है:

DATABASES = {
    'default': {
        "NAME": "localDatabase",
        "ENGINE": "django.contrib.gis.db.backends.postgis",
        "USER": "test",
        "PASSWORD": "test",
        "HOST": "127.0.0.1",
        "PORT": "5432",
    },
}

इसके साथ, टेस्ट चलाने पर test_localDatabase नाम का एक अस्थायी डेटाबेस उपयोग होता है। न तो TEST["NAME"] सेट करने की ज़रूरत है, न ही किसी फ़िक्स्चर में सेटिंग्स को हाथ से बदलने की। जैसे settings.DATABASES["default"]["NAME"] = "test_local" जैसी कोशिशें Django के ऑटोमैटिक प्रोविजनिंग को बायपास कर देती हैं और कनेक्शन फेल होने लगते हैं, क्योंकि आपके लिए डेटाबेस बनाया ही नहीं जाता।

यह क्यों महत्वपूर्ण है

टेस्ट आइसोलेशन आकस्मिक डेटा भ्रष्टाचार से बचाता है और नतीजों को दोहराने योग्य बनाता है। Django को अस्थायी टेस्ट डेटाबेस बनाने और मैनेज करने देना हर रन में साफ़ स्टेट सुनिश्चित करता है और सूक्ष्म क्रॉस-टेस्ट दखल से बचाता है। टेस्ट के भीतर एक्टिव डेटाबेस का नाम प्रिंट करना एक सरल सेफ़्टी चेक है कि आप लोकल वर्किंग डेटाबेस पर नहीं चल रहे।

मुख्य बातें

टेस्ट डेटाबेस निर्माण Django पर छोड़ दें। conftest.py में ऐसे कस्टम ओवरराइड्स हटाएँ जो डेटाबेस प्रोविजनिंग का तरीका बदलते हैं, और जब विशेष नाम की ज़रूरत न हो तो TEST["NAME"] हार्डकोड करने से बचें। यदि कभी संदेह हो कि कौन-सा डेटाबेस उपयोग में है, तो टेस्ट के अंदर connection.settings_dict["NAME"] प्रिंट करें; यह वर्तमान सक्रिय कनेक्शन दिखाता है, जो टेस्ट रन के दौरान स्वतः बने test_<name> डेटाबेस की ओर होना चाहिए।

यह लेख StackOverflow पर प्रश्न (लेखक: Geo) और उसी उपयोगकर्ता Geo द्वारा दिए गए उत्तर पर आधारित है।