LOGIN
התחברות או הרשמה
Avatar
להמשך הרשמה ידנית – לחץ על כפתור ההרשמה, להרשמה/כניסה מהירה בעזרת חשבון רשת חברתית – לחץ על הלוגו בכותרת

אפס סיסמה - שכחתי את שם המשתמש

שם משתמש
סיסמה
זכור אותי

he icon   en icon

גיל זילברפלד

גיל זילברפלד

רביעי, 27 אוגוסט 2014 10:30

תכונות הטסט: עקבות

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

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

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

אנחנו רוצים לדעת שללא כל קשר ל:

  • איפה בעולם הטסטים רצים
  • מתי הם רצים
  • על איזו חומרה
  • מי מריץ אותם
  • כמה פעמים כבר הרצנו
  • באיזה סדר הם רצים
  • לבד או בקבוצה
  • ובכל תנאי סביבה אפשריים

התוצאה שלהם לא תושפע.

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

יש רק בעיה קטנה: אין דבר כזה בידוד מושלם.

האם מצב הזכרון זהה בכל הרצה?

האם הדפדפן השאיר קבצים זמניים, ולכן הדיסק מלא בדיוק באותה מידה?

האם עבר אותו זמן מאז אתחול המערכת בפעם הקודמת?

אנחנו לא יודעים.

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

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

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

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

נשמע קיצוני? ומה קורה אם אני עובד מול database מלוכלך, כשהבדיקות לא נסמכות על מצב קודם? האם צריך תמיד להתחיל מאותו database?

כדברי הנשיא לשעבר: כן ולא.

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

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

תמיד יש איזשהו שיווי משקל של ROI בין הערכת הסיכון וטיפול מראש כדי שלא יקרה.

ההחלטה היא עלינו.

במקור הופיע בבלוג שלי.

בפעם הבאה: עוד על בידוד

שני, 18 אוגוסט 2014 14:42

תכונות הטסט: תחזוקה

לאורך השנים שנאתי את המילה תחזוקתיות (maintainability) בקונטקסט של טסטים. טסטים הם ניתנים לתחזוקה כמו כל קוד אחר. זה מגיע בסוף לנקודה שנשבר לנו ואנחנו כותבים את הכל מחדש. אבל עד אז – אנחנו יכולים לשנות את הקוד.

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

אז מדוע אנחנו מדברים עדיין על טסטים "תחזוקתיים"?

אחת הבעיות היא שאנחנו לא מסתכלים על קוד של טסטים כקוד "אמיתי". הם לא חלק מ קוד production.

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

המחיר הוא הקשור לתחזוקה. זה הוא מחיר עתידי נוסף על עבודת משנה, שנחשבת פחות.

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

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

לתחזוקה יש אכן מחיר גבוה. אבל לא בגלל זה.

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

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

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

maintenance-code

מה יקרה אם נרצה לשנות בקוד הנבדק את השם PositiveCalculatorל Calculator?

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

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

מה קורה שבשפות עם שמן קומפילציה ארוך כמו C++ ? או שפות script שנותנות פידבק רק בזמן ריצה?

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

אז כיצד ניתן להנמיך את מחיר התחזוקה?

העצה הבדוקה היא "לא לצמד את הקוד לטסט".

אבל כפי שראינו בדוגמא הפשוטה ביותר – טסטים תמיד מצומדים לקוד. רמת הצימוד (coupling) ומנגנוני הפידבק משפיעים על כמה משימות "תחזוקה" תהיינה לנו. הנה כמה טיפים כדי להנמיך את המחיר:

  • בדקו תוצאות, לא אלגוריתמים. מכיוון שהטסטים כבר מצומדים ממילא, ככל שנדע פחות בתוך הטסטים על המימוש בקוד, תהיינה פחות בעיות. טסטים שבירים פחות כאשר הם לא נסמכים על קריאות בתוך הקוד. במקום זאת, נתייחס לקוד כאל קופסא שחורה, למרות שאנחנו יודעים איך הקוד עובד. בנוסף, טסטים אלה יהיו גם קריאים יותר.
  • עבודה מול ממשק חשוף. בדיקה צריכה להיעשות מבחוץ פנימה, ולא לבדוק פונקציות פנימיות. ננסה לשמור את שמות הפונקציות הפנימיות, והחתימות שלהן בתוך הקופסא השחורה. אם אתם מרגישים שאין ברירה, הפכו את הפונקציות הפנימיות לחיצוניות באובייקט אחר. עצם החשיפה יהפוך אותן לקלות לשינוי.
  • חסכו ב asserts. ספציפי מדי עבור קריטריון מעבר הטסט, במיוחד כשמדובר באימות של קריאת פונקציות על תלויות, יכול להביא לשבירת טסטים. האם אנחנו באמת צריכים לבדוק שפונקציה נקראה 5 פעמים, או שדי לבדוק קריאה אחת לפחות? האם כשהיא נקראה, מעניינים אותנו הערכים שנשלחו אליה, או שאנחנו יכולים להסתפק בתחום ערכים? ככל שהטסטים הופכים למדויקים יותר, אנחנו מוסיפים רמות של צימוד, וכתוצאה מזה הזדמנויות לשבירת טסטים ללא צורך. זכרו שבמקרה של שבירה, נרצה שהטסטים יתנו לנו כמה שיותר מידע לפתירת הבעיה. אך אם אנחנו לא מרוויחים את המידע הנוסף כתוצאה מה assert המדויק, אפשר להנמיך את קריטריון המעבר.
  • השתמשו בכלי refactoring טובים. וכלי פיתוח טוב. ועבדו בשפות שתומכות באלו. אם לא, הפידבק על שגיאות מתאחר, ומחיר התחזוקה עולה.
  • השתמשו פחות ב mocking. שימוש בכלי mocking הוא כמו שימוש בקרני רנטגן. הם טובים למטרתם, אבל חשיפת יתר היא רעה. Mocks יוצרים צימוד יתר בין הקוד לטסט. אנחנו שוב נסמכים על אלגוריתם פנימי, שיכול להשתנות, ואתו גם הטסט.
  • אל תשתמשו ב mocks ידניים – אלו הגרועים ביותר. מלבד המקרה שהם מאד פשוטים, הם מעודדים העתקה של הקוד לתוך הטסט. כלי mock מעודדים עבודה מול ממשק.

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

במקור הופיע בבלוג שלי.

בפעם הבאה: עקבות.

חמישי, 07 אוגוסט 2014 11:57

תכונות הטסט: בידול

בידול לא בא לבד. הוא דורש קבוצה של טסטים.

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

ה- Waterfall. לפני שנים רבות, כשלא הכרתי תהליך אחר, הייתי כותב מסמכי SDD. אלו מסמכי detailed design משוקצים שכתבנו לקומפוננטות ולפיצ'רים. כמובן שאף אחד לא אהב אותם, המינוח הפורמלי, האורך, ואפילו הריח. אבל היה להם יתרון: כדי לכתוב אותם, צריך לחשוב על הבעיה קודם (כמו TDD). כשקראנו את המסמכים, הם היוו נקודת פתיחה לשאלות "מה יקרה אם..." – מה יקרה אם הקשר יתנתק? מה אם הקומפוננט לא מאותחל בזמן?

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

ובחזרה לעתיד.

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

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

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

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

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

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

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

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

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

בפעם הבאה: תחזוקה.

רביעי, 30 יולי 2014 09:48

תכונות הטסט - מיקוד

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

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

נסתכל על מקרה פשוט נוסף, הפעם באותה פונקציה. יש לנו class בשם PositiveCalculator עם פונקציה Add שמחברת שני מספרים חיוביים, או זורקת exception אם הם לא כל כך חיוביים:

אנחנו יכולים לכתוב את הטסטים הבאים:

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

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

אז מה אפשר לעשות?

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

"אבל לא אני כתבתי את הקוד! מה לעשות?"

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

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

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

נוכל להפוך טסטים לממוקדים יוצר גם בשיטות הבאות:

  • Assert אחד לכל טסט – ה Assert הוא קריטריון המעבר של הטסט. אם בודקים רק קריטריון אחד, בדרך כלל הטסט ממוקד יותר, כי הוא בודק פחות מסלולים בקוד. אם יש מספר קריטריונים בטסט אחד, פצלו את הטסט למספר טסטים.
  • בדיקת תהליכים קצרים – בקוד legacy, כזה שצברנו לאורך שנים, מאד מפתה לבדוק תהליכים ארוכים, כי הקוד עושה המון דברים. הוא גם לא חושף נקודות כניסה לפעולות יחידניות. נסו לבדוק תהליכים קצרים יותר, ולבדוק אובייקטים קטנים יותר. נסו לשבור את התהליכים הארוכים לתת-תהליכים קצרים יותר אם טסטים קצרים יותר. אם השתמשתם בטסטים ארוכים כדי לשפר את הקוד, אפשר לבדוק בנפרד את החלקים הקטנים בדיוק רב יותר.
  • שימוש בכלי mocking – אם יש תלויות בקוד שעושות הרבה דברים, ולכן גורמות לתהליכים להתארך, זייפו אותם. לא רק שהטסט הופל לממוקד יותר, הוא גם ירוץ מהר יותר.
  • בדיקת כיסוי הקוד – עדיף בצורה ויזואלית. כלי פיתוח ובדיקות המאפשרים לנו לראות את הכיסוי על הקוד עצמו, מוסיפים רמז ויזואלי לגבי הקוד שהטסט עובר בו. בקוד טריוויאלי אין לכך יתרון גדול, אבל בקוד מסובך, ניתן להשוות את מסלולי הקוד השונים אל מול הטסטים המתאימים. ע"י תהליך אלימינציה, ניתן להגיע לבעיות. כמו כן, פידבק ויזואלי עוזר לדעת כמה הטסטים באמת ממוקדים, ואם הם לא – שפרו אותם.

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

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

בפעם הבאה: בידול.

 

שני, 21 יולי 2014 13:48

,תכונות הטסט - מהירות

אחד הסיפורים שאני אוהב לחזור עליהם (שוב ושוב) הוא חווית ה TDD הראשונה שלי. לפני שנים רבות, סיימתי לקרוא את הספר המופלא של קנט בק "Test Driven Development by Example". הייתי בטוח ש TDD הוא התשובה לכל מכאוביי.

באותו זמן עבדתי על רכיב תקשורת, ואמרתי לעצמי: למה לא לנסות את הדבר החדש הזה על עצמי?

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

הטסט הראשון שכתבתי, שלח הודעה ובדק שהגיעה.

משהו כזה:

אנחנו מדברים על מהירות. אם טסט כזה רץ כ-3 שניות, כמה זמן ייקח להריץ 100 כאלה?

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

ניקח לדוגמא ריצה שלוקחת כ- 15 דקות. ונניח שאני אדם סבלני מאד. נניח.

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

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

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

אני קורא לזה "The Death Spiral of Doom". רבים וטובים נופלים בה. רבים לא מנסים לטפס שוב.

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

פידבק מהיר הוא חובה!

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

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

מה אפשר לעשות?

  • ניתוח. אורך הריצה מופיע בכל דוח הרצה. חפשו את הטסטים האיטיים והוציאו להרצה נפרדת.
  • סדר. פצלו הרצות לאיטיות ומהירות. השאירו את האיטיות לתהליך הבנייה האוטומטי, כדי שניתן יהיה לבצע הרצה מהירה על המכונה הלוקאלית.
  • זיוף. שימוש ב mocking עוזר רבות להגברת מהירות . אם יש תלות ברכיב איטי (כמו MSMQ), השתמשו בכלי mocking.
  • עוד קצת סדר. לא כל הטסטים חייבים להיות חלק ממערך הבדיקות לנצח. אם יש קוד שלא נוגעים בו, אבל ריצת הטסטים עליו אורכת 5 דקות, הפסיקו להריץ אותם, או העבירו אותם לריצת לילה.
  • שדרוג. תופתעו כמה עזרה נותנת חומרה משודרגת להרצה. המחיר של הוספת זכרון או דיסק מהיר יחסית למהירות שתקבלו הוא קטן.

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

הפוסט הזה הוא חלק מסדרת "תכונות הטסט". בפעם הבאה: דיוק.

הכותב מעביר הדרכות וליווי בנושאי בדיקות ואג'ייל – המעוניינים יכולים לפנות דרך האתר:http://www.gilzilberfeld.com/p/contact.html

 

Test Speed

שישי, 18 יולי 2014 09:54

תכונות הטסט: קריאות

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

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

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

טסטים אפקטיביים מקצרים את התהליך הזה. מה שמייחד אותם היא הקריאות.

זהו הפוסט השני מתוך סדרת התכונות כפי שתוארו בפוסט "מי בודק את הבדיקות". הפוסט מתורגם מהבלוג שלי: http://www.gilzilberfeld.com/2014/07/test-attribute-2-readability.html

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

 

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

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

לדוגמא:

@Test

public void divideCounterBy5Is25() { ...

אפשר להבין מהשם מה הטסט בודק (תסריט של חלוקה של Counter), הפרטים של המקרה (חלוקה ב-5), והתוצאה המצופה (25).

אם השם נשמע כמו משפט –מעולה. שמות טובים מגיעים מתיאור וורבאלי.

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

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

Test@

public void divideCounterBy0Throws() { ...

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

 

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

הנה כמה טיפים כדי להפוך קוד של בדיקה לקריא יותר:

  • טסטים צריכים להיות קצרים. כ 10-15 שורות.
  • אם האתחול (setup) ארוך, יש להוציא אותו לפונקציות פנימיות, וגם להן לתת שם קריא.
  • הימנעו משימוש באתחול שרץ לפני כל טסט, כמו פונקציות @before ב JUnit. עדיף להשתמש בקריאות ישירות לפונקציות מתוך הטסט. כשמסתכלים על הטסט, אתחול וניקוי אמורים להיות חלקים רציפים ממנו. אחרת, נצטרך לחפש בקוד, וגם להניח הנחות (לא בהכרח נכונות). גם לדבג טסטים כאלה זה לא כיף גדול, מכיוון שנכנסים לתוך הטסט עם קונטקסט "מפתיע".
  • הימנעו משימוש ב base test class. קד משותף הנמצא בהן מחביא מידע רלוונטי להבנת התסריטים.
  • הבליטו את ה assert, תנאי המעבר של הטסט.
  • וודאו שהשם והגוף תואמים.
  • ניתוח לוקח הזמן, והדרך הגרועה (והאיטית) לעשות אותו היא לדבג. טסטים (וגם קוד) צריכים להיות קריאים מספיק כדי שלא נצטרך לעשות זאת.

 

אנחנו לא אובייקטיבים. אנו חושבים שהקוד והבדיקות שלנו כל כך טובים, שכולם יבינו אותם.

בדרך כלל אנחנו טועים.

התשובה לבעיה בעולם האג'ילי היא פידבק.

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

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

 

הפוסט הבא: מהירות.

הכותב מעביר הדרכות וליווי בנושאי בדיקות ואג'ייל – המעוניינים יכולים לפנות דרך האתר:http://www.gilzilberfeld.com/p/contact.html

שלישי, 15 יולי 2014 12:03

תכונות הטסט: נכונות

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

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

הפוסט מתורגם מהבלוג שלי: http://www.gilzilberfeld.com/2014/07/test-attributes-1-validity.html

 

נתחיל מנכונות. למעשה זו לא הייתה התכונה הראשונה שחשבתי עליה. מה הסיכוי שאנחנו נכתוב טסט לא נכון?

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

שאלה יותר מעניינת היא: כיצד אנחנו מזהים את המקרה שבו כתבנו טסט לא נכון?

יש כמה מקרים שבהם אנחנו מזהים את הטעויות:

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

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

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

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

הפוסט הבא: קריאות.

הכותב מעביר הדרכות וליווי בנושאי בדיקות ואג'ייל – המעוניינים יכולים לפנות דרך האתר:http://www.gilzilberfeld.com/p/contact.html

 

ראשון, 13 יולי 2014 14:56

מי בודק את הבדיקות?

פוסט זה תורגם מהבלוג שלי: http://www.gilzilberfeld.com/2014/07/how-to-test-your-tests.html

כשאנו כותבים טסטים (בדיקות), אנחנו מתמקדים בתסריט אותו אנו רוצים לבדוק, ואז אנו מקדדים אותו.

פשוט, לא?

כך פועל המוח שלנו, אנחנו לא יכולים להתמקד בהרבה דברים בו זמנית. TDD (פיתוח מונחה בדיקות) בנוי על התקדמות צעד-צעד בגלל זה.

אם משתמשים ב TDD, וגם אם לא, כשסוף-סוף יש לנו טסט, אנחנו צריכים לעצור, ולבצע הערכה, הפעם עבור הטסט עצמו.

נשתמש בטבלה הבאה:

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

מהירות

כמה מהירה ההרצה? האם הטסט יאט את ההרצה של שאר הבדיקות?
דיוק האם כשהטסט נופל, האם הוי מכוון אותי אל המקום הבעייתי בקוד, או שאצטרך לדבג?
בידול איך התסריט הזה שונה מאחיו? האם אפשר להבין את ההבדל רק מלהסתכל על הטסטים?
תחזוקה כמה עבודה כרוכה בשינוי הטסט כאשר הדרישות משתנות?
עקבות האם הטסט מנקה אחרי עצמו? האם הוא משאיר קבצים, ערכי registry, זכרון תפוס או תהליכים שעדיין לא הסתיימו, ויכולים להשפיע על ריצת טסטים אחרים?
חוסן כמה קל לשבור את הטסט? אילו שינויים (variants) אנחנו מאפשרים, והאם השינויים האלה סבירים?
עצמאות האם לטסט יש תלויות (שעון המחשב, המעבד, קבצים או נתונים) שעלולים לשנות את תוצאת הריצה בהתאם להיכן או מתי הוא רץ?
בידוד האם הטסט תלוי במצב שלא מתואר מפורשות בקוד? אם התשובה היא כן, האם המצב הזז תמיד מתקיים כשהתסריט יקרה?

אם אחת התכונות הללו מוערכות לא ע"פ הסטנדרטים שלכם (ואני מעריך שלקוראים סטנדרטים גבוהים) – תקנו את הטסטים.

אתם בטח שואלים: כל זה, עבור כל טסט?

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

כלכלת בדיקות בסיסית. עשו זאת עכשיו.

הכותב מעביר הדרכות וליווי בנושאי בדיקות ואג'ייל – המעוניינים יכולים לפנות דרך האתר: http://www.gilzilberfeld.com/p/contact.html