कोर्स
जैसे-जैसे आपके Python प्रोजेक्ट जटिलता और आकार में बढ़ते हैं, सुदृढ़ परीक्षण का महत्व भी उतना ही बढ़ता है। परीक्षण यह सुनिश्चित करने में मदद करता है कि आपका कोड अपेक्षा के अनुरूप काम कर रहा है और एप्लिकेशन के सभी हिस्सों में गुणवत्ता बनी रहती है।
जब आपका एप्लिकेशन विभिन्न प्रकार के डेटाबेस या OpenAI GPT या Anthropic Claude जैसे बाहरी API से इंटरैक्ट करता है, तो इन निर्भरताओं का परीक्षण करना चुनौतीपूर्ण हो सकता है। यहीं पर मॉकिंग काम आती है।
मॉकिंग आपको ऑब्जेक्ट्स, फंक्शंस और व्यवहारों का अनुकरण करने देती है, जिससे आपके परीक्षण अधिक पृथक और पूर्वानुमेय बनते हैं। यह आपको उस विशिष्ट फ़ंक्शन पर ध्यान केंद्रित करने में भी मदद करती है जिसे आप परीक्षण करना चाहते हैं, और उसे उन निर्भरताओं से अलग करती है जो जटिलता या अप्रत्याशित व्यवहार ला सकती हैं।
इस ब्लॉग में, आप pytest-mock का उपयोग करना सीखेंगे, जो pytest के लिए एक शक्तिशाली प्लगइन है, अपने Python परीक्षणों में कुशलतापूर्वक मॉक लागू करने के लिए। ट्यूटोरियल के अंत तक, आप अपने टेस्ट केस में मॉकिंग तकनीकों को जोड़ने के लिए तैयार होंगे, जिससे वे और ज्यादा सक्षम बनेंगे।
pytest-mock क्या है?
pytest-mock लोकप्रिय Python टेस्टिंग फ्रेमवर्क pytest के लिए एक प्लगइन है जो मॉकिंग क्षमताओं तक आसानी पहुंच प्रदान करता है। यह Python के बिल्ट-इन unittest पर आधारित है और परीक्षण के दौरान मॉकिंग की प्रक्रिया को सरल बनाता है।

pytest फ्रेमवर्क लोगो
pytest-mock पठनीयता बढ़ाता है और pytest-स्वदेशी दृष्टिकोण के साथ परीक्षणों में मॉक लागू करना आसान बनाता है। चाहे आपको व्यक्तिगत फ़ंक्शंस, क्लास मेथड्स, या पूरे ऑब्जेक्ट्स को मॉक करना हो, pytest-mock अतिरिक्त जटिलता के बिना प्रभावी परीक्षण के लिए आवश्यक लचीलापन प्रदान करता है।
pytest-mock की पठनीयता का एक प्रमुख कारण इसका घोषणात्मक परीक्षण शैली है, जो आपको अपने परीक्षणों में अपेक्षित कॉल और व्यवहार सीधे निर्दिष्ट करने देती है। इससे कोड लिखना, पढ़ना और बनाए रखना आसान हो जाता है, जबकि यह उन क्रियान्वयन परिवर्तनों के प्रति लचीला बना रहता है जो कोड के बाहरी व्यवहार को नहीं बदलते।
pytest-mock सेट करना
अपने प्रोजेक्ट के लिए वर्चुअल एनवायरनमेंट बनाना एक सर्वोत्तम प्रथा है ताकि निर्भरताएँ पृथक रहें। यदि आपने अभी तक नया एनवायरनमेंट सेट नहीं किया है, तो आप निम्नलिखित कमांड का उपयोग करके एक conda एनवायरनमेंट बना सकते हैं:
# create a conda environment
conda create --name yourenvname python=3.11
# activate conda environment
conda activate yourenvname
pytest और pytest-mock स्थापित करना
अपने एनवायरनमेंट में pip या conda का उपयोग करके सीधे pytest और pytest-mock स्थापित करें:
pip install pytest
pip install pytest-mock
इंस्टॉलेशन की पुष्टि करना
pytest और pytest-mock स्थापित करने के बाद, निम्नलिखित कमांड चलाकर सत्यापित करें कि इंस्टॉलेशन सफल रहा:
pytest --version
pytest-mock के साथ मॉकिंग की मूल बातें
मॉकिंग को किसी घटक का "डमी" संस्करण बनाने के रूप में समझा जा सकता है जो वास्तविक व्यवहार की नकल करता है।
यह विशेष रूप से तब सहायक होती है जब आपको यह जांचना हो कि फ़ंक्शंस या क्लासेस डेटाबेस या बाहरी API जैसे बाहरी घटकों के साथ कैसे इंटरैक्ट करते हैं।
मॉकिंग का परिचय
एक मॉक ऑब्जेक्ट किसी वास्तविक ऑब्जेक्ट के व्यवहार का अनुकरण करता है। इसे अक्सर यूनिट टेस्ट में घटकों को पृथक करने और उनके व्यवहार का परीक्षण करने के लिए उपयोग किया जाता है, बिना आश्रित कोड को चलाए।
उदाहरण के लिए, आप किसी डेटाबेस से वास्तव में कनेक्ट किए बिना डेटाबेस कॉल का अनुकरण करने के लिए मॉक ऑब्जेक्ट का उपयोग कर सकते हैं।
मॉकिंग विशेष रूप से उपयोगी होती है जब वास्तविक ऑब्जेक्ट:
- बनाना या कॉन्फ़िगर करना कठिन हो।
- उपयोग करने में समय लेने वाला हो (जैसे, रिमोट डेटाबेस तक पहुँचना)।
- ऐसे साइड इफेक्ट हों जिन्हें परीक्षण के दौरान टालना चाहिए (जैसे, ईमेल भेजना, लागत आना)।
mocker फ़िक्स्चर का उपयोग
pytest-mock एक mocker फ़िक्स्चर प्रदान करता है, जो मॉक ऑब्जेक्ट बनाना और नियंत्रित करना आसान बना देता है। mocker फ़िक्स्चर शक्तिशाली है और आपके pytest परीक्षणों में एकीकृत हो जाता है।
💡फिक्स्चर क्या होते हैं?Python में, फिक्स्चर पुन: प्रयोज्य घटक होते हैं जो परीक्षणों के लिए आवश्यक संसाधनों (जैसे डेटाबेस या फाइलें) को सेटअप और टियरडाउन करते हैं। वे स्थिरता सुनिश्चित करते हैं, दुहराव कम करते हैं, और टेस्ट सेटअप को सरल बनाते हैं। |
आइए एक उदाहरण देखें:
def fetch_weather_data(api_client):
response = api_client.get("https://api.weather.com/data")
if response.status_code == 200:
return response.json()
else:
return None
fetch_weather_data फ़ंक्शन डेटा प्राप्त करने के लिए एक बाहरी वेदर API पर निर्भर करता है, लेकिन परीक्षण के दौरान API कॉल करने से अनावश्यक लागत आ सकती है।
इसे टालने के लिए, आप अपने परीक्षणों में API के व्यवहार का अनुकरण करने हेतु मॉकिंग का उपयोग कर सकते हैं, ताकि एप्लिकेशन का परीक्षण बाहरी कॉल किए बिना हो सके। यहाँ मॉकिंग कैसे मदद कर सकती है:
def test_fetch_weather_data(mocker):
# Create a mock API client
mock_api_client = mocker.Mock()
# Mock the response of the API client
mock_response = mocker.Mock()
mock_response.status_code = 200
mock_response.json.return_value = {"temperature": "20C", "condition": "Sunny"}
# Set the mock API client to return the mocked response
mock_api_client.get.return_value = mock_response
# Call the function with the mock API client
result = fetch_weather_data(mock_api_client)
# Assert that the correct data is returned
assert result == {"temperature": "20C", "condition": "Sunny"}
mock_api_client.get.assert_called_once_with("https://api.weather.com/data")
test_fetch_weather_data() में, हम mocker फ़िक्स्चर का उपयोग करके एक मॉक API क्लाइंट mock_api_client और mock_response बनाते हैं। मॉक प्रतिक्रिया को इस तरह कॉन्फ़िगर किया गया है कि वह 200 स्टेटस कोड और मौसम संबंधी JSON डेटा लौटाकर वास्तविक API कॉल की नकल करे।
मॉक API क्लाइंट की get मेथड को यह मॉक प्रतिक्रिया लौटाने के लिए सेट किया गया है। जब fetch_weather_data() का परीक्षण होता है, तो वह वास्तविक API कॉल करने के बजाय मॉक API क्लाइंट के साथ इंटरैक्ट करता है।
यह तरीका सुनिश्चित करता है कि फ़ंक्शन अपेक्षा के अनुरूप व्यवहार करता है, और परीक्षण तेज, किफायती और बाहरी प्रणालियों से स्वतंत्र रहते हैं।
अंत में दिया गया assert कथन सत्यापित करता है कि लौटाया गया डेटा अपेक्षित आउटपुट से मेल खाता है।
आखिरी पंक्ति, mock_api_client.get.assert_called_once_with("https://api.weather.com/data"), विशेष रूप से जाँचती है कि मॉक API क्लाइंट की get मेथड बिल्कुल एक बार और सही आर्ग्युमेंट के साथ कॉल हुई। यह पुष्टि करने में मदद करता है कि फ़ंक्शन अपेक्षित तरीके से API के साथ इंटरैक्ट कर रहा है, जिससे परीक्षण में अतिरिक्त वैधता जुड़ती है।
pytest-mock के सामान्य उपयोग के मामले
मॉकिंग विभिन्न परिदृश्यों में उपयोगी होती है, विशेषकर यूनिट टेस्ट में जहाँ आपको विशिष्ट फ़ंक्शंस या घटकों को पृथक करना होता है। आइए कुछ सामान्य उपयोग के मामलों पर नज़र डालें:
1. फ़ंक्शंस या क्लास मेथड्स को मॉक करना
आपको किसी ऐसे फ़ंक्शन का परीक्षण करने की आवश्यकता हो सकती है जो अन्य फ़ंक्शंस या मेथड्स पर निर्भर हो, पर आप परीक्षण के दौरान उनके व्यवहार को नियंत्रित करना चाहते हों। मॉकिंग आपको उन निर्भरताओं को पूर्व-परिभाषित व्यवहार से बदलने देती है:
# production code
def calculate_discount(price, discount_provider):
discount = discount_provider.get_discount()
return price - (price * discount / 100)
# test code
def test_calculate_discount(mocker):
# Mock the get_discount method
mock_discount_provider = mocker.Mock()
mock_discount_provider.get_discount.return_value = 10 # Mocked discount value
# Call the function with the mocked dependency
result = calculate_discount(100, mock_discount_provider)
# Assert the calculated discount is correct
assert result == 90
mock_discount_provider.get_discount.assert_called_once()
इस परीक्षण में, discount_provider की get_discount() मेथड को एक पूर्व-निर्धारित मान (10%) लौटाने के लिए मॉक किया गया है। इससे calculate_discount() फ़ंक्शन discount_provider के वास्तविक इम्प्लीमेंटेशन से अलग हो जाता है।
2. समय-निर्भर कोड को मॉक करना
जब उन फ़ंक्शंस का परीक्षण किया जाता है जिनमें समय या विलंब शामिल होता है, तो समय-संबंधी मेथड्स को मॉक करने से प्रतीक्षा या वास्तविक समय में बदलाव से बचा जा सकता है:
# production code
import time
def long_running_task():
time.sleep(5) # Simulate a long delay
return "Task Complete"
# test code
def test_long_running_task(mocker):
# Mock the sleep function in the time module
mocker.patch("time.sleep", return_value=None)
# Call the function
result = long_running_task()
# Assert the result is correct
assert result == "Task Complete"
यहाँ time.sleep को पैच किया गया है ताकि परीक्षण के दौरान वास्तविक विलंब बायपास हो जाए। इससे परीक्षण 5 सेकंड प्रतीक्षा किए बिना तेज़ी से चलता है।
3. ऑब्जेक्ट के गुणों को मॉक करना
कभी-कभी, आप परीक्षण के दौरान अलग-अलग अवस्थाओं का अनुकरण करने के लिए किसी ऑब्जेक्ट के गुणों या प्रॉपर्टीज़ के व्यवहार को मॉक करना चाह सकते हैं:
# production code
class User:
def __init__(self, name, age):
self.name = name
self.age = age
@property
def is_adult(self):
return self.age >= 18
# test code
def test_user_is_adult(mocker):
# Create a User object
user = User(name="Alice", age=17)
# Mock the is_adult property
mocker.patch.object(User, "is_adult", new_callable=mocker.PropertyMock, return_value=True)
# Assert the mocked property value
assert user.is_adult is True
इस उदाहरण में, User क्लास की is_adult() प्रॉपर्टी को, वास्तविक आयु की परवाह किए बिना, True लौटाने के लिए मॉक किया गया है। यह उन परिदृश्यों के परीक्षण में उपयोगी है जो ऑब्जेक्ट की अवस्थाओं पर निर्भर करते हैं।
pytest-mock के साथ उन्नत मॉकिंग तकनीकें
जब आप बुनियादी मॉकिंग में सहज हो जाते हैं, तो pytest-mock अधिक जटिल परिदृश्यों को संभालने के लिए उन्नत क्षमताएँ प्रदान करता है।
1. साइड इफेक्ट्स मॉक करना
मॉक्स केवल रिटर्न वैल्यू का अनुकरण ही नहीं करते—वे अपवाद उठाने या रिटर्न वैल्यू को गतिशील रूप से बदलने जैसे व्यवहारों की भी नकल कर सकते हैं। यह मॉक्स में “साइड इफेक्ट्स” जोड़कर किया जाता है:
import pytest
# production code
def process_payment(payment_gateway, amount):
response = payment_gateway.charge(amount)
if response == "Success":
return "Payment processed successfully"
else:
raise ValueError("Payment failed")
# test code
def test_process_payment_with_side_effects(mocker):
# Mock the charge method of the payment gateway
mock_payment_gateway = mocker.Mock()
# Add side effects: Success on first call, raise exception on second call
mock_payment_gateway.charge.side_effect = ["Success", ValueError("Insufficient funds")]
# Test successful payment
assert process_payment(mock_payment_gateway, 100) == "Payment processed successfully"
# Test payment failure
with pytest.raises(ValueError, match="Insufficient funds"):
process_payment(mock_payment_gateway, 200)
# Verify the mock's behavior
assert mock_payment_gateway.charge.call_count == 2
side_effect प्रॉपर्टी मॉक को क्रमिक कॉल पर अलग-अलग मान लौटाने या अपवाद उठाने देती है। इस उदाहरण में, पहली कॉल में charge "Success" लौटाता है, जबकि दूसरी कॉल में ValueError उठता है। यह एक ही परीक्षण में विभिन्न परिदृश्यों का परीक्षण करने में उपयोगी है।
2. फ़ंक्शंस पर स्पाइंग
स्पाइंग आपको यह ट्रैक करने देती है कि किसी वास्तविक फ़ंक्शन को कैसे कॉल किया गया—कितनी बार और किन आर्ग्युमेंट्स के साथ। यह तब विशेष रूप से उपयोगी होती है जब आप सुनिश्चित करना चाहते हैं कि कोई फ़ंक्शन अपेक्षित रूप से कॉल हो, और साथ ही उसका मूल लॉजिक भी निष्पादित हो:
# production code
def log_message(logger, message):
logger.info(message)
return f"Logged: {message}"
# test code
def test_log_message_with_spy(mocker):
# Spy on the info method of the logger
mock_logger = mocker.Mock()
spy_info = mocker.spy(mock_logger, "info")
# Call the function
result = log_message(mock_logger, "Test message")
# Assert the returned value
assert result == "Logged: Test message"
# Verify the spy behavior
spy_info.assert_called_once_with("Test message")
assert spy_info.call_count == 1
spy() मेथड किसी वास्तविक मेथड के कॉल होने के तरीके का रिकॉर्ड रखती है, बिना उसके व्यवहार को बदले। यहाँ हम लॉगर की info मेथड पर स्पाइ करते हैं ताकि यह सुनिश्चित हो सके कि उसे सही संदेश के साथ कॉल किया गया, जबकि मेथड सामान्य रूप से निष्पादित भी होती रहे।
pytest-mock का उपयोग करने के सर्वोत्तम अभ्यास
मॉकिंग शक्तिशाली है, लेकिन लापरवाही से उपयोग करने पर आपके परीक्षणों में समस्याएँ आ सकती हैं। यहाँ कुछ सर्वोत्तम अभ्यास दिए गए हैं जिनका पालन करने की मैं सलाह देता/देती हूँ:
1. ओवर-मॉकिंग से बचें
बहुत अधिक मॉक का उपयोग करने से परीक्षण नाज़ुक हो सकते हैं, जो आंतरिक कोड के बदलने पर टूट जाते हैं—भले ही बाहरी व्यवहार न बदले। केवल उसी चीज़ को मॉक करने का प्रयास करें जिसकी आवश्यकता हो, और जहाँ संभव हो वास्तविक इम्प्लीमेंटेशन पर निर्भर रहें।
ओवर-मॉकिंग आपके परीक्षणों को कोड के इम्प्लीमेंटेशन विवरणों से कड़ी तरह जोड़ सकती है, जिसका मतलब है कि मामूली रीफ़ैक्टरिंग भी अनावश्यक रूप से परीक्षणों को असफल कर सकती है। इसके बजाय, केवल सिस्टम के उन्हीं हिस्सों को मॉक करने पर ध्यान दें जो बाहरी हैं या जिनके महत्वपूर्ण साइड इफेक्ट हैं।
2. स्पष्ट नामकरण परंपराएँ अपनाएँ
ऑब्जेक्ट्स को मॉक करते समय, अपने मॉक्स के लिए वर्णनात्मक नामों का उपयोग करें। इससे आपके परीक्षण अधिक पठनीय और बनाए रखने में आसान बनते हैं। उदाहरण के लिए, mock_function() जैसे सामान्य नामों के बजाय mock_api_response() जैसे अधिक वर्णनात्मक नामों का उपयोग करें।
स्पष्ट नामकरण परंपराएँ दूसरों को प्रत्येक मॉक के उद्देश्य को समझने में मदद करती हैं, भ्रम को कम करती हैं, और समय के साथ टेस्ट सूट को बनाए रखना आसान बनाती हैं।
3. परीक्षणों को सरल और केंद्रित रखें
प्रत्येक परीक्षण को एक ही व्यवहार या परिदृश्य पर केंद्रित होना चाहिए। इससे डीबगिंग सरल होती है और आपके परीक्षण समझने में आसान बने रहते हैं। एक अच्छी तरह केंद्रित परीक्षण अधिक स्पष्ट फ़ीडबैक देने की संभावना रखता है, जिससे समस्याओं की पहचान और उन्हें ठीक करना आसान हो जाता है।
निष्कर्ष
इस ब्लॉग में, हमने pytest-mock के साथ मॉकिंग की मूल बातें देखीं और सीखा कि इसका उपयोग करके हम अपने Python परीक्षणों में कैसे सुधार ला सकते हैं। हमने फ़ंक्शंस और मेथड्स की बुनियादी मॉकिंग से लेकर साइड इफेक्ट्स जोड़ने और फ़ंक्शंस पर स्पाइंग जैसी उन्नत तकनीकों तक सब कुछ कवर किया।
मॉकिंग विश्वसनीय और अनुरक्षित परीक्षण बनाने के लिए आवश्यक उपकरण है, खासकर जब आप जटिल प्रणालियों या बाहरी निर्भरताओं के साथ काम कर रहे हों। अपने प्रोजेक्ट्स में pytest-mock को शामिल करके, आप अधिक पृथक, पूर्वानुमेय और बनाए रखने में आसान टेस्ट लिख सकते हैं।
अपनी टेस्टिंग कौशल को और बढ़ाने के लिए, मैं आपको DataCamp के Introduction to Testing in Python निःशुल्क कोर्स को देखने की सलाह देता/देती हूँ।
FAQs
मॉकिंग वास्तविक घटकों के परीक्षण से कैसे अलग है?
मॉकिंग वास्तविक घटकों को सिम्युलेटेड घटकों से बदल देती है, जिससे बाहरी सिस्टम या वास्तविक डेटा पर निर्भर हुए बिना पृथक और किफायती परीक्षण संभव हो पाता है।
क्या मैं pytest-mock को अन्य टेस्टिंग फ्रेमवर्क के साथ उपयोग कर सकता/सकती हूँ?
pytest-mock विशेष रूप से pytest के लिए डिज़ाइन किया गया है और उसके साथ सहजता से एकीकृत होता है, लेकिन यह Python के unittest.mock पर आधारित है, जिसे स्वतंत्र रूप से भी उपयोग किया जा सकता है।
क्या pytest-mock में मॉकिंग के लिए फ़िक्स्चर अनिवार्य हैं?
mocker जैसे फ़िक्स्चर मॉकिंग की दक्षता बढ़ाते हैं, लेकिन आप बिना फ़िक्स्चर के भी pytest में सीधे unittest.mock का उपयोग कर सकते हैं—हालाँकि एकीकरण कम सहज होगा।
