בשלהי העשור הקודם, כשנחשפתי לראשונה לתשתית אוטומציה (עוד בימים התמימים והעליזים של QTP ו-VB-Script), נחשפתי לראשונה למתילדה.
מתילדה הייתה שמה של Mega Function, פונקציה אחת שעושה הכל ! בפרוייקט שלנו, כל מה שנרצה כל מה שנבקש – מתילדה תספק.
כיום, עפ"י Design Pattern שונים ומתודולוגיות פיתוח מודרניים (לדוגמא – SOLID), אנחנו לומדים שבשביל לכתוב תשתית אוטומציה אנחנו צריכים לפרק הכל לגורמים ולעבד כל מחלקה וכל פונקציה לתהליך נפרד שמנגן אח"כ בצורה הרמונית ע"י מקרה הבדיקה (Test@).
עם שיטוטיי הרבים בין חברות ההייטק השונות, נחשפתי להמון טכניקות ומתודולוגיות כתיבה, ואת מתילדה שלראשונה ראיתי לפני כבר עשור, ראיתי כמה פעמים (בודדות) בכמה הזדמנויות שונות.
אתם זוכרים את תחילת הסרט הראשון של "שר הטבעות" ? מה נאמר בפתיח ?
" הכל החל עם חישול הטבעות הגדולות. שלוש טבעות ניתנו לאלפים… שבע טבעות ניתנו לשרי-הגמדים… ותשע טבעות ניתנו לבני האדם… אך כל עונדי הטבעות רומו כי עוד טבעת אחת חושלה… הטבעת האחת שתשלוט בכל האחרות… "
מתילדה היא אותה "טבעת אחת" , מתילדה היא פונקציה אחת שתשלוט בכולן:
הרעיון שעומד מאחורי שם זה שלקוח מהאגדות הינו ליצור ממשק אחיד לטסטים שלנו שרק מולו נעבוד ע"י יצירת פונקציה אחד שתקטלג ותחלק את הבקשות שלנו, כמו מעין דיספנסר. פונקציה זו תקבל מספר פרמטרים ותדע לפיהם למי לשלוח את הבקשה, זאת אומרת היא תקרא לפונקציות עזר אחרות (יש כאלו שקוראים להם פלאגינים) לביצוא הפעולות.
לדוגמא, נגיד שנרצה להיכנס לאתר של וויקיפדיה להזין ערך חיפוש "היי-טק" , לבחור מתוך ה-Drop Down את הערך "עברית" , להקיש על כפתור החיפוש ובעמוד החדש שנטען – לוודא כי קיימת המילה "תדיראן"
מקרה הבדיקה שלנו יכול להיראות כך, בהנחה שהשתמשנו בתשתית מופרדת ומחולקת למחלקות שונות עם אוסף פעולות שונה:
@Test public void test01() { mainPage.updateText(pageObjects.searchField, "היי-טק"); mainPage.updateCombo(pageObjects.dropDownMenu, "עברית"); mainPage.clickSearch(pageObjects.searchButton); resultsPage.verifyText(pageObjects.textField, "תדיראן"); }
אנו רואים כאן עבודה עם Page Objects תוך קריאה לאובייקט של המחלקה שמייצגת את אותו איזור היא מכסה באפליקציה, למשל: mainPage זהו אובייקט של המחלקה המייצגת את הדף הראשי של וויקיפדיה.
במקרה הנוכחי, אנו רואים כאן קריאה ל-2 אובייקטים בלבד, אך זהו מקרה דוגמא מאוד פשוט ומאוד קצר, יכולים להיות לנו במקרה בדיקה קריאה לעשרות אובייקטים שונים – דבר היכול לבלבל את מיישמי האוטומציה בבואם לכתוב טסטים. מקרה בדיקה ארוך יכול להסתכם בפעולות:
נווט לאתר: חנות וירטואלית -> בצע רישום בטופס המיועד -> היכנס לאתר עם ההרשאות -> בחר קטגוריית מוצרים -> חפש מוצר -> פלטר את החיפוש (מחיר, מיקום ג"ג, דמי משלוח…) -> בחר מוצר -> עבור לעגלת הקניות -> אשר את עגלת הקניות -> עבור לדף התשלומים -> הכנס פרטים אישיים ומספר כרטיס אשראי -> אשר נתונים -> בצע רכישה.
במקרה כזה אנו נאלץ לקרוא ל-13 אובייקטים שונים (וישנם מקרי בדיקה ארוכים עוד יותר…) מ-13 מחלקות שונות.
נעים מאוד – מתילדה!
מתילדה זהו רק שם, אתם יכולים לקרוא לפונקציה זו איך שאתם רוצים כמובן, כשנחליט לעבוד עם Mega Functions כמו מתילדה בבואנו לכתוב טסטים, אנחנו נעבוד אם ה-API של מתילדה בלבד. לא 13 אובייקטים שונים, לא עשרות רבות של פונקציות, אלא פונקציה אחת שהיא מאחורי הקלעים כבר תדע לקרוא לפונקציה המתאימה בהתאם לפרמטרים שנשלח לה – דיספנסר , כבר אמרתי ?
הנה לדוגמא מקרה פשוט של מימוש מתילדה:
public void mathilda(WebElement elem, action act, String value, controller cont) { switch(act) { case click: { click(elem); break; } case update: { if(cont.text.toString().equals("text")) updateText(elem, value); else updateDropDown(elem, value); break; } case goTo: { driver.get(value); break; } } }
מה אנחנו רואים כאן?
פונקציה שמקבלת אובייקט מסוג WebElement עליו היא תבצע את הפעולה, סוג הפעולה (enum) שיכול להתבטא ב-click, update Text, updateDropDown, goTo וכו'… , הערך אותו נרצה להזין לאלמנט (אם נרצה), וסוג ה-controller (שגם הוא enum)
בפנים אנחנו ממפים את הפעולה וקוראים לפונקציה המתאימה בהתאם למיפוי
פונקציית מתילדה תשב בפרוייקט שיכלול קבצים ומחלקות נוספות, למשל במחלקה אחרת, אנו נוכל לממש את הפונקציות של הפעולות, כך:
public void click(WebElement elem) { elem.click(); } public void updateText(WebElement elem, String value) { elem.sendKeys(value); } public void updateDropDown(WebElement elem, String value) { Select select = new Select(elem); select.selectByVisibleText(value); }
זהו אמנם מקרה מאוד פשטני, אך בקלות ניתן כעת להרחיב את יכולות הפונקציות , להוסיף להם reports או דיווח ל-logs, להכניס waits עם timeouts וכו'…
גם במקרה של מתילדה, אין שום מניעה להמשיך ולעבוד עם Page Objects שכן אין קשר בין שני הדברים, במחלקות ה-Page Objects אנו נזהה את האלמנטים ואילו מחלקת מתילדה תהיה אחאית על ביצוע הפעולות עליהם (אותם אלמנטים כאמור יישלחו למתילדה כפרמטרים)
login Page לדוגמא:
public class POLogin { @FindBy(how = How.NAME, using = "username") public WebElement userName; @FindBy(how = How.NAME, using = "password") public WebElement password; @FindBy(how = How.CSS, using = "button[type='submit']") public WebElement submit; }
וכמובן הטסטים שלנו יראו כך (דוגמא לכניסה לאתר PHP Travels) :
@Test public void Test1() { mathilda(null, action.goTo, "https://www.phptravels.net/login", null); mathilda(login.userName, action.update, "user@phptravels.com", controller.text); mathilda(login.password, action.update, "udemouser", controller.text); mathilda(login.submit, action.click, "", null); }
כפי שניתן לראות הכל מתנהל על ידי קריאה למתילדה שאחראית ל-
One Function to Rule Them All !!!
אני חייב לציין כי כיום מעטים המקומות בהם משתמשים ב-Mega Functions , הקהל הגדול הולך עפ"י שיטות מודרניות יותר (בצדק), אך בכל זאת, זוהי מתודולוגיה שבעבר היתה פופולרית מאוד וזכאית גם כן לפוסט משלה