יסודות Pytest
בוא נתחיל בבדיקות עם Pytest. כפי שציינו ביחידה הקודמת, Pytest ניתן להגדרה גבוהה ויכול לטפל בחבילות בדיקה מורכבות, אבל זה לא דורש הרבה כדי להתחיל לכתוב בדיקות. למעשה, מסגרת קלה יותר מאפשרת לך לכתוב בדיקות, טוב יותר.
בסוף סעיף זה אתה אמור לקבל את כל מה שאתה צריך כדי להתחיל לכתוב את הבדיקות הראשונות שלך ולהפעיל אותם עם Pytest.
מוסכמות
לפני שנצלול לבדיקות כתיבה, עלינו לכסות כמה ממוסכמות הבדיקה ש- Pytest מסתמך עליו.
אין כללים קשיחים רבים בנוגע לקבצי בדיקה, מדריכי בדיקה או פריסות בדיקה כלליות ב- Python. על-ידי הכרת כללים אלה, באפשרותך לנצל את היתרונות של גילוי וביצוע אוטומטיים של בדיקה ללא צורך בתצורה נוספת.
בדיקת קבצי מדריך כתובות ובדיקה
מדריך הכתובות הראשי לבדיקות הוא בדיקות הכתובות. באפשרותך למקם ספריה זו ברמת הבסיס של הפרוייקט, אך גם לא יוצא דופן לראות אותה לצד מודולי קוד.
הערה
במודול זה, נשתמש כברירת מחדל בבדיקות בבסיס של פרוייקט.
בוא נראה כיצד הבסיס של פרוייקט Python קטן בשם python jformat נראה:
.
├── README.md
├── jformat
│ ├── __init__.py
│ └── main.py
├── setup.py
└── tests
└── test_main.py
הבדיקות של נמצאות בבסיס הפרוייקט עם קובץ בדיקה יחיד. במקרה זה, קובץ הבדיקה נקרא test_main.py. דוגמה זו מדגימה שתי מוסכמות קריטיות:
- השתמש במדריך בדיקות כדי למקם קבצי בדיקה וספריות בדיקה מקוננות.
- קבצי בדיקת קידומת עם בדיקה. הקידומת מציינת שהקובץ מכיל קוד בדיקה.
זהירות
הימנע משימוש test (צורת יחיד) כשם הספריה. שם test הוא מודול Python, ולכן יצירת ספריה בשם זהה תעקוף אותה. השתמש תמיד בריבוי tests במקום.
פונקציות בדיקה
ארגומנט חזק לשימוש ב- Pytest הוא שהוא מאפשר לך לכתוב פונקציות בדיקה. בדומה לקבצי בדיקה, יש להוסיף קידומת לפונקציות בדיקה עם test_. הקידומת test_ מבטיחה ש- Pytest יאסוף את הבדיקה ויבצע אותה.
כך נראית פונקציית בדיקה פשוטה:
def test_main():
assert "a string value" == "a string value"
הערה
אם אתה מכיר את unittest, ייתכן שיפתיע אותך לראות את השימוש assert בפונקציית הבדיקה. אנו מכסים קביעות רגילות ביתר פירוט מאוחר יותר, אך עם Pytest, אתה מקבל דיווח עשיר על כשלים עם קביעות רגילות.
מחלקות בדיקה ושיטות בדיקה
בדומה למוסכמות עבור קבצים ופונקציות, מחלקות בדיקה ושיטות בדיקה משתמשות במוסכמות הבאות:
- מחלקות בדיקה כוללות קידומת של
Test - שיטות בדיקה מותקנות בקידומת עם
test_
ההבדל העיקרי עם ספריית unittest ב- Python הוא שאין צורך בירושה.
הדוגמה הבאה משתמשת בקידומות אלה ובמוסכמות אחרות למתן שמות ל- Python עבור מחלקות ושיטות. הוא מדגים כיתת בדיקה קטנה בודקת שמות משתמש ביישום.
class TestUser:
def test_username(self):
assert default() == "default username"
הפעל בדיקות
Pytest הוא גם מסגרת בדיקה וגם רץ בדיקה. רץ הבדיקה הוא קובץ הפעלה בשורת הפקודה כי (ברמה גבוהה) יכול:
- בצע את אוסף הבדיקות על-ידי איתור כל קבצי הבדיקה, מחלקות הבדיקה ופונקציות הבדיקה עבור הפעלת בדיקה.
- הפעל בדיקה על-ידי ביצוע כל הבדיקות.
- עקוב אחר כשלים, שגיאות והעברת בדיקות.
- ספק דיווח עשיר בסוף הפעלת בדיקה.
הערה
מאחר ש- Pytest הוא ספריה חיצונית, יש להתקין אותה כדי להשתמש בה.
בהינתן תוכן זה בקובץ test_main.py, נוכל לראות כיצד Pytest פועל בעת הפעלת הבדיקות:
# contents of test_main.py file
def test_main():
assert True
בשורת הפקודה, באותו נתיב שבו קיים קובץ test_main.py, ניתן להפעיל את pytest הפעלה:
$ pytest
=========================== test session starts ============================
platform -- Python 3.10.1, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /private/tmp/project
collected 1 item
test_main.py . [100%]
============================ 1 passed in 0.00s =============================
מאחורי הקלעים, Pytest אוסף את הבדיקה לדוגמה בקובץ הבדיקה ללא צורך בתצורה.
הצהרת ההצהרה העצמתית
עד כה, דוגמאות הבדיקה שלנו משתמשות בשיחה assert פשוטה. בדרך כלל, ב- Python, assert המשפט אינו משמש לבדיקות, מכיוון שחסר בו דיווח מתאים כאשר הקביעה נכשלת. עם זאת, Pytest אינו כולל מגבלה זו. מאחורי הקלעים, Pytest מאפשר לפורסט לבצע השוואות עשירות מבלי לאלץ את המשתמש לכתוב קוד נוסף או לקבוע תצורה של דבר.
באמצעות המשפט הרגילassert, באפשרותך להשתמש באופרטורים של Python; לדוגמה, >, , <, !=>=, או <=. כל האופרטורים של Python חוקיים. יכולת זו עשויה להיות התכונה החיונית ביותר של Pytest: אינך נדרש ללמוד תחביר חדש כדי לכתוב קביעות.
בוא נראה כיצד זה מתורגם בעת התמודדות עם השוואות נפוצות עם אובייקטי Python. במקרה זה, לעבור על דוח הכשל בעת השוואת מחרוזות ארוכות:
================================= FAILURES =================================
____________________________ test_long_strings _____________________________
def test_long_strings():
left = "this is a very long strings to be compared with another long string"
right = "This is a very long string to be compared with another long string"
> assert left == right
E AssertionError: assert 'this is a ve...r long string' == 'This is a ve...r long string'
E - This is a very long string to be compared with another long string
E ? ^
E + this is a very long strings to be compared with another long string
E ? ^ +
test_main.py:4: AssertionError
Pytest מציג הקשר שימושי סביב הכשל: שגיאה בתחילת המחרוזת ותו נוסף במילה. אך מעבר לכל המחרוזות, Pytest יכול לעזור עם אובייקטים ומבני נתונים אחרים. לדוגמה, כך הוא פועל עם רשימות:
________________________________ test_lists ________________________________
def test_lists():
left = ["sugar", "wheat", "coffee", "salt", "water", "milk"]
right = ["sugar", "coffee", "wheat", "salt", "water", "milk"]
> assert left == right
E AssertionError: assert ['sugar', 'wh...ater', 'milk'] == ['sugar', 'co...ater', 'milk']
E At index 1 diff: 'wheat' != 'coffee'
E Full diff:
E - ['sugar', 'coffee', 'wheat', 'salt', 'water', 'milk']
E ? ---------
E + ['sugar', 'wheat', 'coffee', 'salt', 'water', 'milk']
E ? +++++++++
test_main.py:9: AssertionError
דוח זה מזהה שהאינדקס 1 (הפריט השני ברשימה) שונה. לא רק שהוא מזהה את מספר האינדקס, הוא גם מספק ייצוג של הכשל. מלבד השוואות פריטים, הוא יכול גם לדווח אם פריטים חסרים ולספק מידע שיציין בדיוק איזה פריט עשוי להיות. במקרה הבא, "milk":
________________________________ test_lists ________________________________
def test_lists():
left = ["sugar", "wheat", "coffee", "salt", "water", "milk"]
right = ["sugar", "wheat", "salt", "water", "milk"]
> assert left == right
E AssertionError: assert ['sugar', 'wh...ater', 'milk'] == ['sugar', 'wh...ater', 'milk']
E At index 2 diff: 'coffee' != 'salt'
E Left contains one more item: 'milk'
E Full diff:
E - ['sugar', 'wheat', 'salt', 'water', 'milk']
E + ['sugar', 'wheat', 'coffee', 'salt', 'water', 'milk']
E ? ++++++++++
test_main.py:9: AssertionError
לבסוף, נראה כיצד הוא פועל עם מילונים. השוואה בין שני מילונים גדולים עשויה להיות מדהימה אם יש כשלים, אך Pytest עושה עבודה מצטיין באספקת הקשר ובפירוט הכשל:
____________________________ test_dictionaries _____________________________
def test_dictionaries():
left = {"street": "Ferry Ln.", "number": 39, "state": "Nevada", "zipcode": 30877, "county": "Frett"}
right = {"street": "Ferry Lane", "number": 38, "state": "Nevada", "zipcode": 30877, "county": "Frett"}
> assert left == right
E AssertionError: assert {'county': 'F...rry Ln.', ...} == {'county': 'F...ry Lane', ...}
E Omitting 3 identical items, use -vv to show
E Differing items:
E {'street': 'Ferry Ln.'} != {'street': 'Ferry Lane'}
E {'number': 39} != {'number': 38}
E Full diff:
E {
E 'county': 'Frett',...
E
E ...Full output truncated (12 lines hidden), use '-vv' to show
במבחן זה, קיימים שני כשלים במילון. אחד הוא שהערך "street" שונה, והערך השני הוא שהערך "number" אינו תואם.
Pytest מזהה באופן מדויק הבדלים אלה (למרות שזהו כשל אחד במבחן יחיד). מאחר שהמילון מכיל פריטים רבים, Pytest משממיט את החלקים הזהים ומציג רק תוכן רלוונטי. בוא נראה מה קורה אם נעשה שימוש באפשרויות -vv כדי להגביר את המלל בפלט:
____________________________ test_dictionaries _____________________________
def test_dictionaries():
left = {"street": "Ferry Ln.", "number": 39, "state": "Nevada", "zipcode": 30877, "county": "Frett"}
right = {"street": "Ferry Lane", "number": 38, "state": "Nevada", "zipcode": 30877, "county": "Frett"}
> assert left == right
E AssertionError: assert {'county': 'Frett',\n 'number': 39,\n 'state': 'Nevada',\n 'street': 'Ferry Ln.',\n 'zipcode': 30877} == {'county': 'Frett',\n 'number': 38,\n 'state': 'Nevada',\n 'street': 'Ferry Lane',\n 'zipcode': 30877}
E Common items:
E {'county': 'Frett', 'state': 'Nevada', 'zipcode': 30877}
E Differing items:
E {'number': 39} != {'number': 38}
E {'street': 'Ferry Ln.'} != {'street': 'Ferry Lane'}
E Full diff:
E {
E 'county': 'Frett',
E - 'number': 38,
E ? ^
E + 'number': 39,
E ? ^
E 'state': 'Nevada',
E - 'street': 'Ferry Lane',
E ? - ^
E + 'street': 'Ferry Ln.',
E ? ^
E 'zipcode': 30877,
E }
על-pytest -vv, הדיווח מגדיל את כמות הפירוט ומספק השוואה פרטנית. לא רק שדוח זה מזהה ומציג את הכשל, אלא גם מאפשר לך לבצע במהירות שינויים כדי לפתור את הבעיה.