יוצא לי לא פעם - כמו לרבים אחרים, אני מניח - לקרוא על טכניקה חדשה לבדיקות, או רעיון שמישהו יישם ונראה לי מעניין לנסות. הבעייה היא שבדרך כלל, בגלל הלחץ בעבודה, עובר שבוע ועוד שבוע, ועוד שבוע... ואחרי כמה זמן אתה מבין שכנראה זה לא הולך לקרות. אבל לפעמים אני מחליט להתעלם מהמיילים המצטברים, לדחות התחייבויות ולהשקיע זמן מרוכז ללמוד או לנסות משהו. לאחרונה השקעתי זמן כזה ב-AltWalker, כלי שמאפשר ליצור מודל של מכונת מצבים בצורה מאוד קלה, ובעזרתו לממש את הטכניקה של "בדיקות מבוססות מודל". בטור הפעם אשתף במה שלמדתי.
בדיקות מבוססות מודל
אפשר לטעון שכל בדיקה שאנחנו מייצרים היא "מבוססת מודל". למשל: במקרים רבים מקרי הבדיקה מוגדרים מתוך הדרישות. כיוון שהדרישות מתארות את המוצר הסופי, אבל אינן המוצר עצמו, הן בעצם סוג של מודל של המוצר. גם בדיקות שייצרנו מתוך הנסיון המצטבר עם תוכנה מבססות את התוצאה הצפויה על פי מודל שיש לנו בראש של “איך התוכנה אמורה להתנהג”. מה שמייחד בדיקות מבוססות מודל הוא שהבדיקות מיוצרות באופן אוטומטי בעזרת מודל שמומש בקוד. המודל מדמה את התנהגות התוכנה הנבדקת או חלק ממנה, בדרך כלל תוך קיצורי דרך (פישוט דרישות) או התעלמות מאילוצים שקיימים עבור הקוד האמיתי. הקלות אלה מאפשרות לייצר את המודל במאמץ קטן בהרבה מלפתח את המוצר הסופי.
חוץ מהמודל צריך לכתוב קוד נוסף שמייצר באופן אוטומטי בדיקות (כלומר, הגדרת ערכים שיועברו למודל כקלט). הרצת הקלט על המודל מייצרת את התוצאה הצפויה. עכשיו אפשר להריץ את אותו הקלט גם על המוצר האמיתי ולהשוות את התוצאות לאלה שהתקבלו מהמודל.
במקרים אחרים המודל מממש "מכונה" שמייצרת קלט שאותו נשלח לקוד הנבדק. אם, למשל, המכונה תייצר קלט תקף עבור התוכנה הנבדקת, נצפה שהתוצאה של העיבוד תהיה נכונה. אם המכונה בנוייה לייצר קלט שגוי, נצפה שהתוצאה תהיה תגובה הולמת לקלט שגוי.
אם כך, לצורך מימוש בדיקות מבוססות מודל, צריך (א) לכתוב את המודל ו-(ב) לכתוב קוד שמשתמש בו ומייצר בדיקות. נו, זה לא מעט עבודה. לשמחתנו, יש כבר כלים שעוזרים בכך. אחד מהם הוא AltWalker, אותו אתאר כאן.
הקוד הנבדק
אדגים את השימוש ב-AltWalker לבדיקת קוד שמטפל בדה-פרגמנטציה (defragmentation) של מסרים בתקשורת בין מחשבים. ברוב הפרוטוקולים מגבילים את אורך המסר שאפשר לשלוח במנה (packet) אחת ממחשב אחד לשני. למשל, בפרוטוקול Ethernet הגודל המקסימלי הוא 1500 בייטים. מה עושים אם יש מסר שאורכו מעל 1500? שוברים אותו לחתיכות (באנגלית זה "fragment"; לא מצאתי תרגום טוב בעברית אז אקרא לזה "שבר"), שכל אחת מהן באורך עד 1500 בייטים (מותר גם פחות), והאחרונה בדרך כלל פחות (מה שנשאר). כל שבר נשלח במנה נפרדת. כמובן שצריך לסמן על כל מנה מהו מספר השבר שבתוכה, כדי שהמחשב המקבל ידע להרכיב מחדש את המסר כולו (אין הבטחה שמנות יגיעו באותו סדר בו הם נשלחו).
המחשב שמקבל את השברים מבצע פעולת דה-פרגמנטציה (defragmentation): חיבור השברים יחד, בסדר הנכון, ליצירת המסר המלא. אנו רוצים לבדוק שקוד הדה-פרגמנטציה במחשב המקבל יודע להתמודד יפה עם קומבינציות רבות של מסרים שבורים.
אפשר כמובן לכתוב בעצמנו קוד שמייצר קומבינציות כאלה, אבל יותר קל ונוח להגדיר מודל שמייצר מנות ושברים. כל הרצה של המודל תייצר מסר שבור חדש. המודל יכתוב לקובץ טקסט את האורך ואת המספר הסידורי של כל מנה במסר, ואחר כך נוכל לכתוב קוד יחסית פשוט ש"מתרגם" את תיאור המנות לממש ביטים שישלחו על הרשת.
הנה דוגמה לתוצאה של הרצה אחת של המודל:
New Message: MsgLen = 5890
FRAG_START, MsgLen=5890 BytesLeft=5890
FRAGMENT 0. FragmentSize=1500 BytesLeft=4390
FRAGMENT 1. FragmentSize=1500 BytesLeft=2890
FRAGMENT 2. FragmentSize=1500 BytesLeft=1390
FRAGMENT 3. FragmentSize=1390 BytesLeft=0
"בבדיקות מבוססות-מודל הבדיקות מיוצרות באופן אוטומטי בעזרת מודל שמומש בתוכנה" |
FRAG_END
השלב הבא יהיה לכתוב קוד שמתרגם טקסט כמו בדוגמה שכאן, לשליחה של מנות. בדוגמה למעלה, שלושת המנות הראשונות יעבירו מטען (payload) של 1500 בייטים כל אחת, והאחרונה 1390 בייטים. כל מנה תכלול את מספר המנה המתאים. נניח שבמנה הראשונה נכתוב 1500 פעמים 'A', בשניה נכתוב 1500 פעמים 'B', וכו'. ביציאה מהקוד של הדה-פרגמנטציה נצפה למסר באורך 5890 בתים שמכיל את הכמויות הצפויות של 'A', 'B', 'C', ו-'D' בסדר הנכון. זה שלב טכני לגמרי ולא אתעכב עליו, אלא אתמקד בדרך שבה מייצרים את המודל.
המודל
התרשים הבא מתאר מכונת מצבים שמייצרת מסרים שבורים:
AltWalker
AltWalker משתמש בשני קבצי קלט. בקובץ הראשון (קובץ json) מתארים את המודל: את המצבים (states) ואת המעברים (transitions) הקיימים ביניהם. עבור כל מעבר אפשר להגדיר תנאי: מה צריך להתקיים על מנת שהמעבר יתבצע. לדוגמה, במכונת המצבים של פרגמנטציה, המעבר ממצב Fragment למצב End יתבצע רק כאשר הערך של BytesLeft יהיה אפס. כמו כן ניתן בקובץ זה להגדיר פקודות פייתון בסיסיות שמתבצעות כאשר מגיעים למצב או כאשר המעבר מתבצע. הפקודות האפשריות מוגבלות לאלה שקיימות בפייתון הבסיסי ללא שום חבילות ותוספות.
הקובץ השני נקרא test.py ("קובץ הבדיקות"). בקובץ זה מגדירים פונקציות פייתון לכל אחד מהמצבים והמעברים. שורות הקוד בפונקציות מתבצעות לאחר ההגעה למצב או לאחר ביצוע המעבר (כלומר, אחרי שהפקודות שהוגדרו בקובץ ה-json של המודל כבר הורצו). כאן כבר אין הגבלות, אפשר לבצע import לספריות של פייתון ולכתוב כל קוד פייתון רצוי.
AltWalker מאפשר שימוש באותם משתנים בקובץ ה-test ובקובץ המודל. המשמעות היא שאפשר לקבוע ערך למשתנה בקובץ הבדיקות, ולהשתמש בו בקובץ המודל. עבור פרגמנטציה, למשל, נרצה לקבוע את גודל המסר באופן אקראי בעזרת ספריית random, ולהשתמש בערך זה כדי להעריך מתי הפרגמנטציה הסתיימה וצריך לקחת את המעבר ל-End. שימוש בספריות (כגון random) אפשרי רק בקובץ הבדיקות, ואילו ההחלטה איזה מעבר לקחת מתבצעת בקובץ המודל. היכולת להשתמש במשתנים זהים בין שני הקבצים מאפשרת מימוש של תנאים מסוג זה ונותנת גמישות רבה בהגדרת המודל.
טיפים
אפשר להתקין את AltWalker על Windows, על MacOS ועל לינוקס. AltWalker היא מעטפת סביב חבילה אחרת (GraphWalker) שמבוססת Java. אפשר להשתמש ישירות ב-GraphWalker, אבל לי היה הרבה יותר נוח לעבוד בפייתון. כמו כן, נאבקתי די הרבה בהתקנה על Windows ובסוף הרמתי ידיים והתקנתי על לינוקס. זה נשמע תיק אם מה שזמין לכם זה מחשב Windows, אבל כאן נכנסת לתמונה חבילת Windows Subsystem for Linux (WSL) שמאפשר להנות מהתקנה של לינוקס על מחשב Windows ולהפתעת הקהל עובדת ממש טוב. אפשר אפילו לבחור את סוג הלינוקס שאתם מעדיפים (linux distribution). לאחרונה עבדתי הרבה עם WSL, ומצאתי שהמערכת יעילה ונוחה. ראו פרטים נוספים על כך בהסברים לדוגמה שיצרתי (הקישור בסוף הטור).
הדוגמה שכתבתי מהווה צעד ראשון ליצירת מקרי בדיקה לקוד של הפרגמנטציה. כאמור, בשלב הבא צריך לקרוא את קובץ הפלט, לתרגם אותו למנות (packets) שישלחו לקוד הנבדק, ולבדוק את התוצאה. אם רוצים אפשר לעשות הכל "במכה אחת", כאשר קוד פייתון בקובץ הבדיקות כבר מבצע את התרגום. אפשר כמובן גם להתממשק למערכת הרצת בדיקות אוטומטית, ועוד ועוד... כיד הדמיון והיצירתיות.
למי שמשתמש ב- Visual Studio Code (מומלץ בחום!), יש תוסף שאפשר להתקין, ש"מתרגם" את קובץ המודל לתיאור גרפי. חפשו “AltWalker” בחלון ה Extensions. ככה נראה המודל שהגדרתי לפרגמנטציה:
הערה: למעשה המודל הסופי מכיל עוד מעבר, מ-END ל-START, על מנת לאפשר למערכת לרוץ ולייצר הרבה מקרי בדיקה, ולא רק אחד (ללא מעבר זה, המודל "נתקע" במצב END אחרי יצירת מקרה בדיקה אחד, ולא יכול להמשיך).
לסיכום
AltWalker (ומן הסתם גם GraphWalker) מאפשרים להתנסות בבדיקות מבוססות מודל. עקומת הלמידה מהירה מאוד (כמה שעות) ותוכלו להגדיר מכונות מצבים ולייצר בעזרתם כמויות רבות של בדיקות במאמץ קטן יחסית. ראו: https://altom.com/testing-tools/altwalker .
יש הרבה חומר על התקנת AltWalker ועל השימוש בו. במקום לחזור על זה, העליתי ל github-דוגמא מלאה של יישום מכונת המצבים של פרגמנטציה, עם הוראות התקנה והרצה (כולל טיפים ומראי מקום). ראו: https://github.com/mmstahl/AltWalker_fragmentation_example