3 דרכים לייצוב הבדיקות האוטומטיות שלנו



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

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

הדבר שאנחנו מדברים עליו כאן נקרא Flaky Tests והוא דבר חם מאוד בעולם האוטומציה היום.

מה זה Flaky Tests?

flaky tests או test flakiness הוא ביטוי המיצג חוסר יציבות של בדיקות אוטומטיות.
אם אותה בדיקה, באותם תנאים פעם אחת עברה ופעם אחרי נכשלה, הבדיקה היא flaky.

תוצאת תמונה עבור ‪test flakiness‬‏


צעדים לייצוב הבדיקות האוטומטיות

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

דרך 1 - הפחתת כמות בדיקות ה - E2E/UI

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

אנחנו חייבים לשים לב שאיננו בודקים באמצעות בדיקת E2E לוגיקה שיכולה להבדק ברמה נמוכה יותר! (Unit tests/integration tests)

עיקרון single responsibility מדגיש ואומר לנו שכל רכיב תוכנה (פעולה/מחלקה) צריך לעשות דבר אחד ורק אחד ואותו עיקרון תקף גם כאשר אנחנו מדברים על בדיקות.

חשוב לזכור!
ככל שיהיו שלבים ל test שלנו, כך אמינותו תהיה נמוכה יותר! זה פשוט מאוד, אבל שאנחנו עושים יותר דברים יש לנו יותר מקומות שאנחנו יכולים לטעות בהם.

דרך 2 - הורדת ה - Sleep מהבדיקות שלנו

ה-sleep הוא חבר טוב כמעט של כל מפתח אוטומציה בתחילת דרכו.
אנחנו כותבים תרחיש בדיקות, מריצים אותו בפעם הראשונה, והוא עובר, מריצים אותו שוב ופתאום הוא נכשל. 
מה קרה?
לפעמים ישר אנחנו יכולים לראות את האיזור הבעייתי ולפעמים אנחנו מגלים שאנחנו תלויים ברכיב אחר (שרת מסוים/Database/דף שיעלה בדפדפן וכו')

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

העניין בתלות ברכיבים שונים הוא שקצב חזרת התשובה מהם משתנה מאוד (בגלל שוני במערכת הפעלה/תוכנה/חומרה/דפדפן/מהירות אינטרנט וכו').

כך אף פעם לא נוכל באמת לדעת כמה אנחנו צריכים לחכות!

מה לעשות במקום?

במקום המתנה של זמן מסוים, נרצה להמתין הפעם לתנאי מסוים.

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

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

דרך 3 - הוספת לוגים

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

אני אוהב לכתוב לוג לפעולות שלי בדרך הבאה:


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

בסוף הפעולה אני רושם ללוג את הערך שמוחזר מהפעולה רגע לפני החזרתו.

חשוב לזכור - אין דבר כזה יותר מדי לוגים! (כמובן שחשוב לסווגם)


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

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

סיכום

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

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

ביצוע הנ"ל יקדם מאוד אותנו מאוד ביציבות התרחישים שלנו ויתרון רבות למערכת הבדיקות שלנו.

אם אתה חושב על עוד דרכים בהן נוכל לייצב את הבדיקות שלנו, רשום אותם בתגובה!


 נתראה בפוסט הבא!




תגובות

  1. מאמר יפה מאד, ואני מסכים עם כל ההמלצות שלך. עם זאת, לפחות ההמלצה הראשונה לא קשורה באופן ישיר ליציבות הטסטים, וכן הייתי מוסיף המלצות נוספות. אגב, הנושא הזה נידון בהרחבה בספר שלי "Complete Guide to Test Automation" (שניתן כבר להזמין ברכישה מוקדמת ב-https://www.amazon.com/Complete-Guide-Test-Automation-Maintaining/dp/1484238311)

    ולהערות שלי ביתר פירוט:
    * להמלצה הראשונה - להפחית את הבדיקות E2E - יש הרבה מאד שיקולים (לכאן ולכאן) מעבר ליציבות הטסטים. זה נכון שבאופן כללי טסטים "קטנים" יותר בד"כ יותר יציבים, אבל א. זה לא תמיד נכון, וב. כשנתקלים בטסט אחד או שניים לא יציבים, אני לא חושב שזאת סיבה לזנוח את הבדיקות E2E רק בגלל זה, אלא לחקור את הסיבה לבעיה ולפתור אותה. מנסיוני, הסיבה העיקרית לחוסר יציבות בבדיקות E2E היא לרוב חוסר בבידוד (Isolation) או חוסר התאמה של האוטומציה לארכיטקטורה של האפליקציה (יש בספר שלי פרק שלם על כל אחד מהנושאים הללו). כמעט תמיד ניתן לכתוב בדיקות E2E יציבות מאד אם מתכננים אותן נכון.
    * ההמלצה השניה נכונה גם ללא קשר ליציבות הטסטים. למעשה, אם במקום לחכות לתנאי מסויים נבצע Sleep ואחריו נבדוק שהתנאי התקיים, אז מבחינת היציבות זה שקול לפתרון של המתנה שהתנאי יתקיים. מעבר לזה שבד"כ אנשים לא בודקים את התנאי לאחר ה-Sleep, הבעיה העיקרית של Sleep היא שכדי שלא נכשל רק בגלל שלא חיכינו מספיק, אני צריכים תמיד לחכות את הזמן של המקרה הגרוע ביותר שהוא לרוב הרבה הרבה יותר גבוה מהזמן הממוצע שהפעולה לוקחת.
    * הוספת לוגים היא למעשה ההמלצה היחידה במאמר שקשורה ישירות ליציבות הטסטים. אני מאד אוהב את הגישה של לכתוב כשפעולה מתחילה וכשהיא מסתיימת, ולכן, בפרוייקט הפתוח Test Automation Essentials (זמין כ-NuGet package ל-.Net) יצרתי מנגנון שמאפשר לכתוב פעולות כשהן מתחילות והסיום שלהן נכתב לבד כשהמתודה מסתיימת. בנוסף לכך שהסיום נכתב אוטומטית, הפעולות מופיעות בלוג בצורה מקוננת (Nested) כך שקל מאד לראות בעיניים איפה פעולה מסויימת מתחילה והיכן היא מסתיימת.

    מבחינת המלצות נוספות שיש לי לטיפול בטסטים לא יציבים (כאמור, בספר זה נידון הרבה יותר בהרחבה):
    1. קודם כל, אל תניח שהבעיה היא בטסט או "בסביבה" - בהחלט יתכן שטסט נכשל מדי פעם בגלל באג אמיתי במוצר, ולכן חשוב מאד לחקור לעומק כל תופעה כזו ולא רק לנסות להמנע ממנה. למעשה, ההיפך הוא הנכון! כל עוד אנחנו לא יודעים בוודאות את הסיבה לבעיה, לא רק שאנחנו לא צריכים לנסות למנוע אותה, אלא אנחנו צריכים לנסות לגרום לה לקרות כמה שיותר על מנת שתהיה לנו יותר אינפורמציה ונוכל לחקור אותה יותר טוב! (אגב, לרוב אנחנו מחביאים את כל הבעיות שאנחנו לא יודעים להסביר במדיוקה מאחורי המונח "בעיית סביבה", אבל ברוב המקרים זה לא ממש נכון)
    2. רצוי לנסות ליצור טסט מיוחד שמשחזר את הבעיה, ומבצע את הפעולה הבעייתי בלופ הרבה פעמים. זאת בנוסף להוספת לוגים, כך שניתן בהדרגה לצמצם עוד יותר את מרחב הבעיה. אם הבעיה מתרחשת בממוצע אחת ל-20 פעמים, אז אם נבצע לופ של 100 נסיונות, הסיכוי שהבעיה תשתחזר כבר קרובה מאד ל-100%, ואז כבר לא ניתן לטעון ש"הבאג לא ניתן לשחזור"...
    3. מעבר ללוגים, יש עוד פרטים שניתן לאסוף, כגון צילומי מסך או וידאו, Page Source (בסלניום), וחשוב מאד: לוגים של המערכת הנבדקת - שכדאי מאד להצליב אותם עם הלוגים של הטסט ולעבות גם אותם בהתאם על מנת לחקור את הבעיה.

    מקווה שתרמתי משהו...

    השבמחק
  2. תודה רבה על התגובה המפורטת ארנון!

    ראשית או ר שכמובן שההמלצות נכתבו בראיית ומנסיוני ואינני אומר שאלו 3 הדרכים היחידות.

    *לגבי בדיקות e2e: לרגע אחד לא אמרתי של בדיקות e2e אין מקום, או שצריך לוותר על בדיקות e2e לא יציבה. האמרה העיקרית שצריך לדבוק בה היא לשים לב שאנחנו לא בודקים את רוב הלוגיקה שלנו דרך בדיקות e2e מכיוון שהדבר פשוט לא נכון (https://martinfowler.com/articles/practical-test-pyramid.html).

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

    *לגבי ה sleep. זה לא בדיוק שקול ללחכות לתנאי שיקרה. Sleep מוגדר כ bad practice, גם מכיוון שהוא פשוט גורם ל tread הראשי של התוכנית לצאת לעצור הכל וללכת לישון. במקרים מורכבים יותר זה עלול להיות בעייתי.

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

    תודה רבה ארנון!

    השבמחק

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

פוסטים פופולריים מהבלוג הזה

תכנות מונחה עצמים | Dependency Inversion Principle

מהם קבצי DLL ואיך להשתמש בהם?

מה ההבדל בין אוטומציה לפיתוח רגיל