כתיבת טסטים אוטומטיים ב- CodedUI עם Page Pattern בעזרת אבסטרקציה

4 בנובמבר 2014

תגיות: , ,
אין תגובות

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

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

אתגר נוסף קשור לגמישות לשינויים ב-UI – אתגר זה יותר קשור לתחזוקת הטסטים.  מפתחי האפליקציה מחליטים יום אחד שאם עד היום ה- UI היה ממומש בטכנולוגיה A, מהיום הוא יממוש בטכנולוגיה B. מה שגורם לכל הטסטים האוטומטים של אותה אפליקציה להשבר. CodedUI עצמו יודע איך להתמודד עם שינוי של קונטרולים מסוימים בתוך האפליקציה, לדוגמא אם עד היום קונרטרול A ישב בהיררכיה מסוימת ועכשיו רוצים לשנות את ההירכייה שלו, CodedUI מתמודד עם זה בהצלחה, ומאפשר את הגמישות הזאת. אבל אם רוצים לשנות את כל הממשק מטכנולוגיה A ל B, זה כבר הרבה יותר מסובך מבחינת הטסטים שכן לא רק שצריך למפות את הקונטרולים מחדש ב UIMap חדש, אלא גם צריך לעבור על הטסטים עצמם, ובכל מקום שיש פניה ישירה לאותו UIMap ישן צריך לשנות את הקוד שיפנה ל UIMap החדש.

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

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

הבידוד הנ"ל מושג על ידי יצירת ממשק (שנקרא לו Page) בין הקונטרולים הספציפיים לבין הטסט האבסקרקטי. הממשק הזה יתן לנו כמה דברים, ראשית זה יגרום לכך שכותבי הטסט לא יהיו מודעים לצורה שבה Page מממש את הפעולות השונות על הקונטרולים הספציפיים, שזה יותר נכון מבחינה תכנותית. ויותר מזה, תחזוקת הטסטים תהיה הרבה יותר קלה. כאשר המפתחים ירצו לשנות את מימוש ה UI, הם יוכלו לעשות את זה בהרבה יותר גמישות, מכיון שעכשיו הדבר היחיד שצריך לשנות זה את ה Page הספציפי ובכך לחשוף את אותן הפונקציות במימוש שונה.

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

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

כמה Pages ו UIMaps יהיה לנו בפרויקט?

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

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

לגבי ה UIMaps הכלל הוא פשוט, כל Page מקבל UIMap משלו בשביל המימוש של אותו Page.

אחרי כל זה בא נראה דוגמה:

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

ברצונינו לכתוב את הטסטס הבא:

1) לחץ על LogIn.

2) הכנס שם משתמש וסיסמא.

3) לחץ על כפתור ה Blogs.

4) חזור לדף הבית

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

נייצר פרויקט אחד מסוג CodedUI Test עבור הטסטים, ועוד אחד מאותו סוג עבור ה UIMaps. ובסוף נייצר פרויקט מסוג Class Library עבור ה Pages השונים.

1

(הדוגמאות אינם עבור אפליקציה אמיתית, אלא רק לצורך דוגמא)

הקוד עבור פרויקט ה Pages:

namespace AutomaionPages.Pages

{

    public class HeaderPage

    {

        #region UIMaps


        private readonly HeaderMap _headerMap;


        #endregion


        public HeaderPage()

        {

            _headerMap = new HeaderMap();

        }


        public void PressOn_Blogs()

        {

var blogs = _headerMap.UIHeaderPageWindow.UIHeaderPageDocument.UINavigationPane.UIBlogsHyperlink;

              Mouse.Click(blogs, new Point(5, 5));

        }


        public void GoToHomePage()

        {

var homePage =    _headerMap.UIHeaderPageWindow.UIHeaderPageDocument.UIGoToHomePagePane;

              Mouse.Click(homePage, new Point(5, 5));

        }


    }





    public class HomePage

    {

        #region UIMaps


        private readonly HomeMap _homeMap;


        #endregion


        public HomePage()

        {

            _homeMap = new HomeMap();

        }


        public void LogIn(string userName, string password)

        {

var _userNameEdit =  _homeMap.UIHomePageWindow.UIHomePageDocument.UIuserNameEdit;

              var _password = _homeMap.UIHomePageWindow.UIHomePageDocument.UIpassword;

var _logInButton =  _homeMap.UIHomePageWindow.UIHomePageDocument.logInButton;

                Keyboard.SendKeys(_userNameEdit, userName);

               Keyboard.SendKeys(_password, password);

                Mouse.Click(_logInButton, new Point(10, 10));

        }


    }

}

הקוד עבור עבור פרויקט הטסטים:

namespace AutomationTests

{


    [CodedUITest]

    public class Tests

    {

        #region Pages


        private HomePage _homePage;

        private HeaderPage _headerPage;


 #endregion


        public Tests()

        {

            _homePage = new HomePage();

            _headerPage = new HeaderPage();

        }


        [TestMethod]

        public void Test1()

        {

            _homePage.LogIn("myUserName", "myPassword");

            _headerPage.PressOn_Blogs();

            _headerPage.GoToHomePage();

        }

        [TestInitialize()]

        public void MyTestInitialize()

        {

BrowserWindow browser = BrowserWindow.Launch(new System.Uri("http://www.myapp.net/"));

        }


        [TestCleanup()]

        public void MyTestCleanup()

        {


        }



    }

}

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

 

שמוליק קלינגרהפוסט נכתב על ידי  שמוליק קלינגר מחברת CodeValue – יועץ בתחום ALM & DevOps, מתמחה בבדיקות אוטומטיות, עם ניסיון הן בתחום פיתוח התוכנה והן בתחום הטמעת מערכת בדיקות במגוון רחב של טכנולוגיות.

חברת CodeValue מובילה בשירותי תוכנה וביצוע פרויקטים, באמצעות בניית הגשר בין טכנולוגיות חדשניות וצרכים עסקיים ספציפיים, תוך הענקת חוויית משתמש ברמה הגבוהה ביותר. חברת CodeValue מבצעת בהצלחה פרויקטיי פיתוח תוכנה במגוון פלטפורמות ומספקת ייעוץ תוכנה ופיתוח המותאם לצרכי הלקוח.. חברת CodeValue מתמקדת במספר נושאים מרכזיים בעולם התוכנה, ביניהם ALM ו DevOps, מחשוב ענן, עולם ההתקנים הניידים, מענה אחוד וכולל לשווקי ה-UI/UX, פיתוח מערכות מידע ועוד. החברה מונה כיום כ-80 עובדים בהם מומחי טכנולוגיה בעלי ניסיון רב, הנחשבים מובילים בתחומם ומוכרים כסמכות מקצועית.

הוסף תגובה
facebook linkedin twitter email

כתיבת תגובה

האימייל לא יוצג באתר. שדות החובה מסומנים *