חלק מהחששות העיקריים שהיו לי בתור מפתח (גם עוד לפני שנכנסתי לתפקיד) היו - מה יקרה אם הקוד יפסיק לעבוד פתאום? מה יקרה אם אכניס שינוי קטן ואז פתאום הכל יקרוס ? איך אדע שהקוד שלי באמת עובד כמו שאני רוצה ? ועוד ועוד.
הרי יצא לי לא מעט להסתבך בעבר עם קוד שיצא מכלל פעולה בגלל שינוי קטן. באוניברסיטה כמעט הרסתי פרוייקט שלם ש"התחיל להשתולל" ו"עשה מה שהוא רוצה" אחרי שהכל היה בסדר...
אז, היום אני יודע שיש כלים שאמורים למנוע בדיוק את זה.
Unit Tests הוא אחד מהם.
כמה מילים לפני ...
כשהתחלתי ללמוד תכנות, גם באוניברסיטה וגם במסגרת העבודה, הייתי בטוח שלמידה של מקצוע הפיתוח הוא ליניארי יחסית. לומדים את כללי היסוד בקורסים או באוניברסיטה ואז מתמקצעים בעבודה וכך בעצם לומדים את כל הדרוש כדי להיות מקצועי בתחום.
להפתעתי, נוכחתי לדעת שזה אינו המצב.
כדי ללמוד ולהתמקצע, מפתח טוב נדרש ללמוד מספר מיומנויות שאינו נלמדות באופן מסודר בשום מקום ועליו ללקט ידע מתחומים שונים. אחד מהתחומים האלה הוא Unit Test והוא אינו חלק חובה מ"מסלול ההדרכה" המקובל עבור מפתחים.
ברוב המקרים, מפתח ימצא את עצמו עם דרישה ממקום העבודה שלו לכתוב Unit Test עבור הקוד שלו והוא עלול להתקשות כיצד לבצע זאת או לשאול את עצמו, למה הוא בכלל צריך לעשות את זה ?
גישות שונות, פתרון דומה
לפני שנתחיל, כדאי לציין שישנן מספר גישות לכתיבת Unit Tests.
גישה אחת, הנקראת TDD - Test Driven Development דוגלת בכתיבת טסטים עוד לפני הקוד והוספתם תוך רוטציה בין פיתוח לכתיבת טסטים.
הרעיון מאחורי השיטה הוא לתכנן את הפונקציונליות של הקוד באמצעות כתיבת טסטים שייכשלו בהתחלה, כיון שהקוד אינו קיים ואז לכתוב את הקוד ולוודא שהטסטים עוברים בסיום כתיבת הפונקציה וכך הלאה.
בשאר השיטות (או כשאין שיטה), המפתחים ימצאו את עצמם כותבים טסטים לקוד שכבר נכתב, לאחר שסיימו את הפיתוח.
צריך להתרגל לעבוד בשיטת TDD והיא אינה טבעית למי שהתרגל להתחיל לכתוב קוד ישר אחרי תכנון.
בכל אופן, כך או כך, המפתח נדרש לדעת לכתוב Unit Test וכדאי שהם יהיו יעילים ויבצעו את העבודה.
אז מה זה Unit Test בעצם ?
המונח Unit Test מתייחס לקוד שמריץ קוד אחר. המינוח Unit מתייחס לכך שהאובייקט הנבחן הוא class object אוטונומי וזהו הקושי הראשון בנושא זה - לבודד את כל אחד מרכיבי הקוד ליחידה עצמאית שתענה לשאילתות של קוד.
מאיפה מתחילים ?
בהנחה והקוד כבר כתוב, עוברים על כל Class בנפרד ובודקים מה תפקידו, לשם מה כתבנו אותו ומה הכי קריטי לנו שהוא יבצע ? ננסה לחשוב אלו פונקציות מועדות יותר לגרימת באגים - שאמינותן חייבת להיות ללא עוררין ונבחר אותן ליעד עבור הטסט.
לאחר מכן, נגדיר באילו מצבים כל פונקציה / מתודה צריכה להבחן. ככל שהפונקציה קריטית יותר, כך נרצה לבחון אותה במקרים מגוונים ושונים, תחת inputs אחרים.
מאחורי כל טסט צריך שיהיה היגיון ומחשבה. הטסטים צריכים להיות קצרים וממוקדים ככל הניתן.
אפשר להניח למשל, ששפת התכנות עובדת ושהקריאה לפונקציות מתבצעת עם הערכים שביקשתם - אין טעם לבדוק שארגומנט שהתקבל אכן יושם באוביקט ולא "הלך לאיבוד".
אפשר להניח למשל, ששפת התכנות עובדת ושהקריאה לפונקציות מתבצעת עם הערכים שביקשתם - אין טעם לבדוק שארגומנט שהתקבל אכן יושם באוביקט ולא "הלך לאיבוד".
יעד טוב לבדיקה יהיה כל דבר מעבר למשתנה שקיבל ערך. כל פעולה חישובית או כל דבר שאתם מסתמכים עליו ושאתם כתבתם. גם אם זה נראה לכם טריביאלי כי זה עובד עכשיו.. אבל מחר מישהו ישנה את הקוד ואולי זה יפסיק לעבוד, אז כדאי שיהיה טסט שייוודא את זה.
יכול להיות שמה שאני כותב כאן נראה עמום מעט, אני אנסה לפרק את זה לדוגמאות בהמשך.
מה זה נותן לי ?
השאלה החשובה ביותר. מכיון שכתיבת טסטים גוזלת זמן פיתוח חשוב, יש ראשית כל להבין את החשיבות של כתיבת הטסטים כדי להצדיק זמן פיתוח יקר שברוב המקרים ניתן במשורה.
פה אנו חוזרים למה שכתבתי בתחילת הפוסט - טסטים (בהנחה והם טובים) מאפשרים קודם כל ביטחון בפיתוח. במיוחד כאשר הקוד שאנו כותבים מורכב ממספר לא זניח של מודולים שונים, כאשר תקלה בכל אחד מהם עשויה לגרום להתמוטטות הקוד כולו.
מי שכבר מנוסה, יודע שפיתוח לא מתבצע מנקודה לנקודה עם התחלה, אמצע וסוף כמו כתיבת סיפור, אלא הוא יותר מתפתח בהדרגתיות כמו בארכיטקטורה. כותבים את היסודות של הקוד - מוודאים שהם תקינים, לאחר מכן מתחילים לכתוב את המודולים לאט ובזהירות.
למרות ההקבלה לארכיטקטורה, פיתוח הוא מאתגר יותר במובן הזה שיסודות בטון שהונחו ככל הנראה יישארו יציבים ולא יתפרקו, אלא שבקוד גם היסודות העמוקים ביותר שלו, יכולים לקרוס ולגרום לקריסת כל שאר הקוד כמו מגדל קלפים.
בניגוד אולי לרושם שמצטבר חיצונית, מפתחים אינם "על-אנושיים" ואין להם יכולת "לקרוא את המטריקס". כאשר הם רואים באג, הם צריכים לעבוד קשה כדי לבודד את הבעיה ולתכנן תיקון. זה יכול לקחת שעות, ימים או שבועות.
כאן נכנס נושא הביטחון. כדי שמפתח יוכל לכתוב קוד בלי פחד, הוא צריך לדעת שהוא יכול לסמוך על החלקים שכתב כבר בקוד החדש שהוא מוסיף מדי יום.
חשוב לדעת שיש לנו את היכולת בכל רגע נתון לשנות כיוון בקוד ולהזיז דברים מבלי לגרום למפולת דומינו דיגיטלית להתרחש מול עיננו המיוסרות.
כאשר סיימנו לכתוב את ה-Unit Tests, אנחנו יכולים לדעת בלחיצת כפתור, האם הקוד שכתבנו ממשיך לבצע את המוטל עליו או שהוא כבר "נדבק" בבאג. במידה ויש טסט שנופל, הוא אמור להצביע על מקור התקלה בצורה ברורה. אנחנו אמורים לדעת בצורה מהירה היכן הבעיה בהתאם לנפילה, לתקן אותה בזריזות ולהמשיך לפתח.
אם נסכם את הדברים לבינתיים, יתרונות Unit Tests הם:
- בתכנון כתיבת קוד, בהנחה שכותבים קודם כל את הטסטים שמייצגים את ההתנהגות הסופית שאנו מצפים מכל מודול בנפרד בקוד. כאשר כל הטסטים יעברו, אנחנו נקבל את "אור ירוק" שנדע שלא שכחנו כלום לממש בקוד ונוכל להמשיך הלאה למודול הבא.
- כאשר כותבים תכנית מרובת מודולים, לאחר שסיימנו כתיבת קוד עיקרי שמשמש בתור תשתית לקוד שעוד לא כתבנו, אנחנו נרצה "לעגן" את ההתנהגות של הקוד שכתבנו כדי לוודא שאם תתבצע נפילה במודול כלשהו, נדע בקלות להצביע על מקור הבעיה, לתקן אותה ולהמשיך הלאה, בלי להסתבך ולהתחיל לחפש את הבאג בכל המודולים מהיסודות עד לראשית הקוד. אפשר להניח שהבאג לא נמצא היכן שיש טסטים שעברו.
- גם כאשר סיימנו לכתוב את הקוד, אנחנו נרצה שהטסטים שנכתוב ידעו להצביע לנו בזריזות על מקור הבאג הבא לכשיצוץ. צריך לזכור, שכשהזיכרון שלנו טרי מכתיבת הקוד, אנחנו יכולים בלי בעיה להגיד היכן נמצא כל חלק בקוד ומה תפקידו, אבל אחרי שבועיים או חודש כשתהיו בפרוייקט אחר, זה לא יהיה כזה פשוט. טסט שנופל, מתריע לכם בצורה מדוייקת על מקור כשל בקוד, או לחילופין, מונע מכם לחפש במקומות שאין צורך.
- גם אם החיים שלכם דבש ואין לכם באגים בקוד, עדיין קשה להמנע ממצב בו תידרשו בעתיד לשנות את הקוד. שינוי של קוד, כאשר כל הטסטים עוברים נותן וודאות שלא "שברתם" כלום בתהליך השינוי, או לחילופין, עוצר אתכם בזמן כדי לתקן את הקוד כשזה עוד פשוט יחסית לביצוע.
נשמע מעולה ! אז למה לא, בעצם ?
בואו נדבר רגע על החסרונות של כתיבת טסטים.
כבר ציינו למעלה - הם גוזלים זמן פיתוח. אם התחייבתם לשבוע עבודה, תצטרכו להוסיף לתחזית עוד מספר ימים.
אם ערכתם שינויים בקוד, עליכם לעדכן את הטסטים או שהם יהפכו ללא רלוונטיים. אם זה קוד חדש - הוא יצטרך טסטים חדשים. אם שיניתם התנהגות, הטסטים הישנים פשוט יפלו - יהיה עליכם לעדכן את הטסטים עם שינוי ההתנהגות החדש.
בנוסף, יש לזכור ש-Unit Tests הם רק בדיקות ממוקדות מודולים בקוד. הן לא יכולות לבדוק מעבר למודול אחד וגם אז הן עובדות על נתונים סינתטיים ולא מעודכנים. אם משהו מחוץ לעולמו של המודול נשבר, כל הטסטים עדיין יעברו.
אם יש צורך לבדוק מעבר למודול בודד, יש צורך לתכנן Integration Tests אך זהו עולם שונה לגמרי ויש בו אתגרים אחרים.
סיכום
הסברנו באופן כללי למה הכוונה כשמדברים על Unit Tests.
דיברנו מעט למה זה כדאי ומהן המגבלות של השיטה.
בפוסטים הבאים, אני אוסיף דוגמאות וכך אני מקווה הנושא יהיה ברור יותר.
בפוסטים הבאים, אני אוסיף דוגמאות וכך אני מקווה הנושא יהיה ברור יותר.
אין תגובות:
הוסף רשומת תגובה