21 בנובמבר 2020

דוגמא ל-UnitTest

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

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

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

בפוסט הזה נשתמש בדוגמא פשוטה של Queue ונדבר על מספר טסטים בסיסים שכדאי לכתוב לו.

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

אנו נניח שמדובר באובייקט סטנדרטי ונקרא לו בשם SimpleQueue.


האובייקט תומך ב-API הבא:

()enqueue - עם ארגומנט של int להוספה ל-queue. מחזיר None.

()dequeue - מוציא את האיבר האחרון בתור. מחזיר None. מוגן מ-Exception אם ה-queue ריק.

()peak - מחזיר את הערך האחרון שהוכנס ל-queue. מחזיר None אם ה-queue ריק.

()size - מחזיר את גודל ה-queue.

()drop - מחיקה של כל ה-queue. מחזיר None.


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

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

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

נתחיל בלקרוא ל-Framework של UnitTest: 

import unittest

מקובל לקרוא ל-class של הטסט כשם ה-class שהיא בודקת עם צירוף המילה Test בהתחלה. כמו"כ נידרש לרשת את ה-class של unittest.TestCase. נקרא ל-class שלנו TestSimpleQueue.

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

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

כדי שה-parser של unitTest יקרא את הטסטים שלנו, נשתמש בקונבציה test_ בתור prefix לכל שם של פונקציה בטסט.
שם הטסט אמור לשקף את מה שהטסט עושה. אין צורך להשתמש בתיעוד. הקוד אמור להיות פשוט וקצר.

כלי נוסף שה-Framework מעמיד לרשותנו הוא שימוש בפונקציות assert מובנות כדי לבצע השוואות בין הערכים שאנו מצפים אליהם מול הערכים שאנו מקבלים מהאובייקט הנבדק.

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

נבדוק מה קורה אם אנחנו מנסים למשוך ערך כשה-queue ריק ונבדוק איזה ערך חוזר לנו ב-peak.

ננסה להכניס מספרים בגדלים שונים. אפשר להשתמש במודול random כדי להכניס מספרים מגדלים שונים (חיובים ושלילים).

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

בכל טסט assert שנכשל, ישנה אפשרות להוסיף פרטים באמצעות string על הערכים שגרמו לכישלון הטסט. מקובל להדפיס את הערכים בפורמט של expect ו-actual.

דבר חשוב אחרון שכדאי לשים לב - כאשר כותבים טסט ה-default שלו הוא pass. כדי שטסט ייכשל צריך לשלוח assert. כאשר טסט עובר - אין הדפסה של ערכים וקשה להבדיל בין טסט שלא עושה כלום לטסט שסיים את כל הבדיקות שלו.

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

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

זוהי ההצעה שלי לטסטים:


אין תגובות:

הוסף רשומת תגובה