Kodokon kodokon.com

pytest के साथ टेस्टिंग: fixtures, पैरामीट्राइज़ेशन, सर्वोत्तम अभ्यास

pytest के साथ मज़बूत टेस्ट संरचित करें: fixture इंजेक्शन, yield-आधारित टियरडाउन, तालिका-आधारित पैरामीट्राइज़ेशन और व्यापक स्कोप के जाल।

9 मिनट · 3 प्रश्न

इस पाठ को Kodokon में खोलें

pytest दो मज़बूत विचारों पर टिका है: परंपरा से खोज (test_*.py फ़ाइलें, test_* फ़ंक्शन) और सादा assert, जिसका इंट्रोस्पेक्शन विफलता पर दोनों पक्षों की वास्तविक वैल्यू दिखाता है - अब assertEqual और उसके तीस रूपों को याद रखने की ज़रूरत नहीं। तुरंत जानने लायक दो औज़ार: pytest.raises, यह जाँचने के लिए कि कोई अपवाद वाकई उठाया गया है, और pytest.approx, बाइनरी राउंडिंग से जूझे बिना फ़्लोट की तुलना करने के लिए।

PYTHON
import pytest

def divide(a: float, b: float) -> float:
    return a / b

def test_nominal() -> None:
    assert divide(8, 4) == 2.0

def test_zero_division() -> None:
    with pytest.raises(ZeroDivisionError):
        divide(1, 0)

def test_floats() -> None:
    assert 0.1 + 0.2 == pytest.approx(0.3)
approx के बिना, बाइनरी फ़्लोट में 0.1 + 0.2 == 0.3 गलत है।

Fixtures pytest का डिपेंडेंसी इंजेक्शन हैं: किसी fixture के नाम वाला एक पैरामीटर घोषित करें, और pytest उसे बनाकर इंजेक्ट कर देता है। yield सेटअप को टियरडाउन से अलग करता है - उसके बाद का कोड तब भी चलता है जब टेस्ट विफल हो जाएscope पैरामीटर (डिफ़ॉल्ट रूप से function, फिर module, session) ऑब्जेक्ट का जीवनकाल तय करता है। साझा fixtures को किसी conftest.py में रखें: वे पूरे फ़ोल्डर में उपलब्ध हो जाते हैं, बिना एक भी इम्पोर्ट के।

PYTHON
import sqlite3

import pytest

@pytest.fixture
def db():
    conn = sqlite3.connect(":memory:")
    conn.execute("CREATE TABLE users (name TEXT)")
    yield conn
    conn.close()

def test_insert(db) -> None:
    db.execute("INSERT INTO users VALUES ('Ada')")
    query = "SELECT name FROM users"
    rows = db.execute(query).fetchall()
    assert rows == [("Ada",)]
conn.close() टेस्ट के बाद चलता है, चाहे वह पास हो या फेल।

@pytest.mark.parametrize किसी टेस्ट को मामलों की तालिका में बदल देता है: हर टपल एक स्वतंत्र टेस्ट बन जाता है, जिसका अपना नाम होता है और जिसे अलग से फ़िल्टर किया जा सकता है। पैरामीटरों का कार्तीय गुणनफल पाने के लिए कई parametrize डेकोरेटर एक के ऊपर एक लगाएँ। pytest.param(..., id='...') के ज़रिए अपने मामलों को पठनीय id दें - कोई विफलता रिपोर्ट स्रोत फ़ाइल खोले बिना समझ में आनी चाहिए।

PYTHON
import pytest

def slugify(text: str) -> str:
    return "-".join(text.lower().split())

@pytest.mark.parametrize(
    ("raw", "expected"),
    [
        ("Hello World", "hello-world"),
        ("  Python  rocks ", "python-rocks"),
        pytest.param("", "", id="empty"),
    ],
)
def test_slugify(raw: str, expected: str) -> None:
    assert slugify(raw) == expected
तीन मामले, रिपोर्ट में तीन अलग-अलग टेस्ट।
BASH
pytest -x --lf
pytest -k "slugify and not empty"
pytest -q --durations=5
--lf केवल विफलताएँ दोबारा चलाता है; --durations धीमे टेस्ट उजागर करता है।

ज्ञान जांच

सुनिश्चित करें कि आपको इस पाठ के मुख्य बिंदु याद हैं।

  1. pytest कैसे जानता है कि किसी टेस्ट को कौन-सा fixture देना है?
    • टेस्ट पर लगे किसी @inject डेकोरेटर की बदौलत
    • फ़ाइल में fixtures के घोषित होने के क्रम से
    • पैरामीटर के टाइप एनोटेशन से
    • पैरामीटर के नाम से: यदि वह किसी दृश्य fixture (मॉड्यूल या conftest.py) से मेल खाता है, तो pytest उसे बनाकर इंजेक्ट कर देता है
  2. किसी fixture में, yield के बाद रखा गया कोड किसलिए है?
    • यह टियरडाउन है: यह टेस्ट के बाद चलता है, भले ही टेस्ट विफल हुआ हो
    • यह कभी नहीं चलता, yield fixture को समाप्त कर देता है
    • यदि सेटअप विफल हो जाए तो यह एक फ़ॉलबैक वैल्यू देता है
    • यह केवल तभी चलता है जब टेस्ट विफल हो
  3. pytest --lf कमांड क्या करता है?
    • यह प्रोजेक्ट में उपलब्ध fixtures की सूची देता है
    • यह केवल उन्हीं टेस्ट को दोबारा चलाता है जो पिछली बार विफल हुए थे
    • यह पहली विफलता मिलते ही सत्र रोक देता है