לדלג לתוכן

כתיבת טסטים בפייתון - מדריך למתחילים

בואו נדבר בכנות. כשמתחילים לתכנת, "בודקים" קוד בצורה אחת - מריצים אותו, מסתכלים על הפלט, ואם זה נראה נכון, ממשיכים הלאה. זה עובד כשהתוכנית שלכם היא עשרים שורות. זה מפסיק לעבוד ברגע שהפרויקט גדל, יש לכם עשרות פונקציות שתלויות אחת בשנייה, ואתם כבר לא זוכרים מה בדיוק בדקתם בפעם הקודמת. אז בואו נדבר על טסטים.

למה בדיקה ידנית לא מספיקה

תארו לעצמכם פונקציה שמחשבת הנחה על מחיר. כתבתם אותה, הרצתם עם כמה מספרים, ראיתם שהתוצאה הגיונית, המשכתם הלאה. שבוע אחר כך שיניתם משהו קטן בקוד כדי להוסיף פיצ'ר חדש, ובלי לשים לב שברתם את חישוב ההנחה. מי יגלה את זה? במקרה הטוב אתם, אחרי שעות של דיבוג. במקרה הרע - משתמש אמיתי, בייצור.

זה בדיוק התפקיד של טסטים אוטומטיים. במקום לבדוק ידנית בכל פעם שמשהו משתנה, כותבים קוד שבודק את הקוד, ומריצים אותו תוך שניות, שוב ושוב, כמה שרוצים.

הכלי - pytest

הספרייה הכי נפוצה לכתיבת טסטים בפייתון היא pytest. מתקינים אותה כמו כל חבילה אחרת:

pip install pytest

עכשיו ניקח דוגמה פשוטה - פונקציה שמחשבת הנחה, בקובץ בשם discount.py:

def apply_discount(price, percent):
    if percent < 0 or percent > 100:
        raise ValueError("אחוז הנחה חייב להיות בין 0 ל-100")
    return price - (price * percent / 100)

עכשיו נכתוב לה טסט, בקובץ נפרד בשם test_discount.py. השם test_ בתחילת הקובץ ובתחילת הפונקציות הוא לא מקרי - pytest מזהה לפיו אילו קבצים ואילו פונקציות הן טסטים:

from discount import apply_discount

def test_apply_discount_regular_case():
    assert apply_discount(100, 10) == 90

def test_apply_discount_zero_percent():
    assert apply_discount(50, 0) == 50

def test_apply_discount_invalid_percent_raises():
    try:
        apply_discount(100, 150)
        assert False, "הייתה אמורה להיזרק שגיאה"
    except ValueError:
        pass

מה זה בעצם assert

assert היא מילת מפתח בפייתון שבודקת האם תנאי מסוים נכון. אם כן - כלום לא קורה, הקוד ממשיך. אם לא - נזרקת שגיאה. זה בדיוק מה שהופך אותה לכלי מושלם לטסטים - כותבים מה אתם מצפים שיקרה, ואם המציאות לא תואמת לציפייה, הטסט נכשל וצועק על זה.

assert 2 + 2 == 4      # עובר בשקט
assert 2 + 2 == 5      # נכשל וזורק AssertionError

הרצת הטסטים

מריצים את כל הטסטים בתיקייה בפקודה אחת:

pytest

הכלי סורק את כל הקבצים שמתחילים ב-test_, מריץ את כל הפונקציות שמתחילות ב-test_, ומדפיס לכם סיכום - כמה עברו, כמה נכשלו, ובאיזו שורה בדיוק. אם משהו נשבר, תראו מיד את השם המדויק של הטסט שנכשל ואת השורה שבה ה-assert לא התקיים.

האלטרנטיבה המובנית - unittest

פייתון מגיע גם עם מודול טסטים מובנה בשם unittest, בלי צורך להתקין כלום. אותו רעיון, אבל בסגנון מונחה עצמים במקום פונקציות פשוטות:

import unittest
from discount import apply_discount

class TestDiscount(unittest.TestCase):
    def test_regular_case(self):
        self.assertEqual(apply_discount(100, 10), 90)

if __name__ == "__main__":
    unittest.main()

זה שימושי לדעת שהוא קיים, בעיקר בפרויקטים ותיקים או ארגוניים שכבר בנויים עליו, אבל ברוב הפרויקטים החדשים אנשים בוחרים ב-pytest כי הוא פשוט יותר לכתוב ולקרוא - אין צורך במחלקות, אין צורך ב-self.assertEqual, פשוט assert רגיל.

התועלת האמיתית - תפיסת רגרסיות

המילה החשובה כאן היא "רגרסיה" - מצב שבו שינוי חדש שובר משהו שכבר עבד. בלי טסטים, רגרסיות מתגלות אצל המשתמשים שלכם, או אצל חבר צוות שממש לא ציפה שהקוד שלו ישבור פיצ'ר בצד אחר לגמרי של המערכת. עם טסטים, כל שינוי שאתם מבצעים רץ מול כל מה שכבר נבדק בעבר, וברגע שמשהו נשבר, אתם יודעים תוך שניות, לפני שהקוד בכלל מגיע לייצור או למישהו אחר בצוות.

זה לא אומר שצריך לכתוב טסט לכל שורת קוד. בהתחלה, מספיק לכתוב טסטים לפונקציות החשובות באמת - חישובים, לוגיקה עסקית, כל דבר שאם הוא נשבר, זה יגרום נזק אמיתי. ההרגל הזה נבנה בהדרגה, כמו כל דבר אחר בתכנות.

מי שעוד לומד את היסודות של פייתון, כדאי לבנות קודם ביטחון בכתיבת פונקציות ולוגיקה בסיסית בקורס התכנות הבסיסי שלנו, ואז לחזור לנושא הטסטים כשהקוד שלכם כבר מספיק מורכב שבאמת שווה לבדוק אותו אוטומטית.

לסיכום

טסטים הם לא "עוד עבודה" מיותרת, הם ההשקעה שחוסכת לכם את הכי הרבה זמן בטווח הארוך. במקום לבדוק ידנית בכל פעם, אתם כותבים פעם אחת מה נכון, ונותנים למחשב לבדוק את זה שוב ושוב, מהר, בלי לשכוח כלום.

הצטרפו לקהילה בדיסקורד