כשמבקשים מסוכן לבנות פיצ'ר, הוא עשוי לסיים בלי לכתוב טסטים ובלי להריץ אותם, אלא אם תבקשו. באפליקציה עם ממשק גרפי, בקשו ממנו לבדוק גם את מה שהמשתמש עושה בפועל. נראה איך כלי בשם Playwright פותח דף ובודק אינטראקציה. אחר כך נסתכל על המבנה של האפליקציה עצמה, כי יש מבנים שמקלים מאוד על הבדיקה. כשהלוגיקה מופרדת מהממשק, אפשר לבדוק את רובה ישירות, בלי לצייר שום מסך. להפרדה הזו יש עוד יתרון. הלוגיקה כבר לא תלויה בספריית הממשק שבמקרה משתמשים בה, כך שאפשר לעדכן או להחליף כל צד בלי לשכתב את השני. נשתמש בתבנית בשם Model View ViewModel כדי להראות איך זה עובד.
Playwright מאפשר לטסט לשלוט בדפדפן אמיתי. הטסט אומר ל-Playwright מה לעשות, ו-Playwright פותח את הדף בדפדפן, לוחץ, מקליד, וקורא את מה שמופיע על המסך.
זה הדף שנבדוק. זו אפליקציית משימות קטנה, ולידה ה-HTML שמאחוריה. יש תיבה לפריט חדש עם התווית New item, כפתור Add, ורשימה, שמתחילה ריקה.
התווית מזהה את התיבה New item. אז הטסט יכול לבקש מ-Playwright את התיבה עם התווית New item.
לכפתור יש את השם Add. אז הטסט יכול לבקש את הכפתור בשם Add. הטסט משתמש בתוויות ובשמות האלה כדי למצוא את הפקדים שהוא צריך, אותם שמות שאדם קורא על המסך. כשנותנים לפקדים תוויות בעלות משמעות, קל יותר לכתוב את הטסטים האלה.
והנה טסט דפדפן שלם לאפליקציית המשימות שלנו, בפייתון. האפליקציה רצה על המחשב הזה, והרשימה שלה מתחילה ריקה. הדף שהטסט מקבל הוא דף דפדפן ש-Playwright מספק. הצעד הראשון פותח בדף הזה את הכתובת של האפליקציה.
אחר כך הטסט מוצא את התיבה New item לפי התווית שלה ומקליד בה Buy milk.
ואז הוא מוצא את הכפתור Add ולוחץ עליו. בדף, Buy milk מופיע עכשיו ברשימה.
השורה האחרונה בודקת שברשימה יש בדיוק פריט אחד, Buy milk. Playwright מחכה לתוצאה הזו, עד timeout מסוים, לפני שהוא מוותר. הבדיקה האחרונה הזו היא מה שהופך את זה לטסט. לא מספיק שהלחיצה הצליחה. אם הפריט לא מופיע אף פעם, הטסט נכשל, והסוכן יכול לקרוא את הכישלון.
ללמוד Playwright מאפס דורש קצת עבודה, ולא חייבים לעשות את כל זה לבד. אפשר לבקש מהסוכן לכתוב ולהריץ את הטסטים האלה בשבילכם. כשפיצ'ר תלוי במה שמשתמש עושה בדפדפן, בקשו במפורש טסטים ב-Playwright שבודקים את האינטראקציות האלה. מה שצריך לציין הוא הפעולה והתוצאה שאתם מצפים לה.
למה בכלל לבקש טסט דפדפן, כשאפשר לבדוק את הקוד שמוסיף פריט? בואו נסתכל על מקרה שבו ההבדל חשוב. לאפליקציית המשימות יש פונקציה שמוסיפה פריט לרשימה. טסט לוגיקה קורא לפונקציה הזו ישירות, בלי דף, ובודק את הרשימה שהיא מחזירה. הטסט הזה עובר.
עכשיו נניח שפונקציית ההוספה עדיין נכונה, אבל הכפתור Add כבר לא מחובר אליה. טסט הלוגיקה עדיין עובר, כי הוא אף פעם לא נוגע בכפתור. משתמש לוחץ Add, ושום דבר לא קורה.
טסט הדפדפן שלנו תופס את זה, כי הוא עובר דרך הכפתור האמיתי ובודק את התוצאה. הוא לוחץ Add, מחכה ש-Buy milk יופיע, ונכשל עם רשימה ריקה. זה הפלט האמיתי מהרצת הטסט הזה מול דף שבו הכפתור מנותק.
זה מה שטסט דפדפן מוסיף. הוא בודק את האינטראקציה, ולא רק את הלוגיקה. לכן כדאי לבקש מהסוכן במפורש לכלול טסטי GUI. הם מכסים התנהגות שטסטים של פונקציות לבדן אף פעם לא מפעילים.
טסט הדפדפן הזה אומר לנו אם הממשק עובד. עכשיו אנחנו רוצים לבדוק את הלוגיקה בלי לפתוח את הדף, וזה תלוי באופן שבו האפליקציה מאורגנת. נשאיר כאן את אפליקציית המשימות ונעבור לדוגמה קטנה יותר, ממיר טמפרטורות. המשתמש מזין טמפרטורה בצלזיוס, לוחץ Convert, ורואה את התוצאה בפרנהייט. עשרים מעלות צלזיוס הן שישים ושמונה מעלות פרנהייט.
אפילו לממיר הקטן הזה יש שלושה תפקידים. הראשון הוא החישוב עצמו, שעובד על טמפרטורה כמספר. התפקיד השני מחזיק את מה שהמסך צריך, כערכים פשוטים בתוכנית. יש מחרוזת עם הטקסט שהוקלד עד עכשיו, פעולת Convert שמריצה את ההמרה, מחרוזת עם התוצאה להצגה, והודעת שגיאה כשהקלט שגוי. התפקיד השלישי הוא הפקדים עצמם. תיבת הקלט מציגה את הטקסט הזה ומאפשרת למשתמש לשנות אותו. הכפתור מריץ את פעולת Convert כשלוחצים עליו. תווית מציגה את התוצאה או את השגיאה. הפקדים האלה קיימים רק כשהמסך מצויר. את הערכים של התפקיד השני אפשר לקבוע ולבדוק בלי לצייר כלום.
החישוב מקבל מספר. המסך צריך גם להתמודד עם טקסט שמישהו עוד מקליד, כולל טקסט שאינו מספר, ולהחליט איזו הודעה להציג. אלה אחריויות שונות. כשמפרידים ביניהן, אפשר לבדוק את שתי הראשונות בלי ליצור אף פקד.
הדרך הזו לארגן אפליקציה נקראת Model View ViewModel, או MVVM. שלושת השמות די דומים, אז בואו נקשר כל אחד לתפקיד שלו בממיר.
ה-Model מחזיק את הנתונים ואת הכללים של האפליקציה, וכאן זו הפונקציה שממירה מצלזיוס לפרנהייט. זה Model במובן הרגיל בתוכנה, לא מודל AI.
ה-View הוא מה שהמשתמש רואה ומפעיל: תיבת הקלט, הכפתור והתוצאה. אלה פקדים שמצוירים על המסך. כאן המשתמש הקליד abc בתיבת הקלט, ותווית מציגה את השגיאה.
ה-ViewModel מחזיק את הערכים והפעולות שה-View צריך. תחשבו עליו כמודל של מצב המסך ושל הפעולות שאפשר לבצע בו. הוא לא המסך עצמו, והוא לא נוגע בפקדים. כשהמשתמש מקליד abc, ה-ViewModel מחזיק את הטקסט הזה כמחרוזת. הוא מחזיק גם את הודעת השגיאה, Enter a number, ותוצאה ריקה.
אז למה בכלל צריך ViewModel? טמפרטורה כמספר שייכת לחישוב. אבל הטקסט שהמשתמש הקליד, למשל abc, הודעת השגיאה עליו, והתוצאה המעוצבת, כולם שייכים לאינטראקציה עם המסך. ה-ViewModel הוא המקום שבו הם נמצאים.
עכשיו החיבורים. ה-View מעביר את הקלט של המשתמש ל-ViewModel ומפעיל את הפקודות שלו, כמו Convert. ה-ViewModel משתמש ב-Model כדי לבצע את החישוב. וה-View מציג את הערכים שה-ViewModel חושף. הקישור בין הפקדים לבין הערכים והפקודות של ה-ViewModel נקרא binding. ה-binding גורם לכך שהקלדה בתיבת הקלט מעדכנת ערך, ושתוצאה חדשה מופיעה על המסך. כך ה-ViewModel עובד עם ערכים, לא עם תיבת טקסט בדף.
בואו נראה איך שלושת החלקים האלה עדיין יוצרים ממיר אחד שעובד. נעקוב אחרי אינטראקציה אחת. ב-View, המשתמש מקליד עשרים ולוחץ Convert. ה-View מעביר את הטקסט ואת פקודת Convert ל-ViewModel, כך שהטקסט ב-ViewModel הוא עכשיו המחרוזת "20".
ה-ViewModel קורא את טקסט הקלט שלו כמספר עשרים, ומבקש מה-Model להמיר אותו.
ה-Model מחזיר את המספר שישים ושמונה.
ה-ViewModel מכין את טקסט התוצאה, שישים ושמונה מעלות פרנהייט, בלי שגיאה. ה-View מציג אותו דרך ה-binding שלו לערך הזה. שימו לב שהחישוב אף פעם לא צריך לדעת איזה פקד מציג את התשובה שלו.
הנה למה המבנה הזה עוזר לנו לבדוק. טסט יכול לעשות את מה שה-View עשה, בלי שום דף פתוח. הוא קובע את הטקסט של ה-ViewModel, מפעיל את Convert, ובודק את הטקסט שהמסך היה מציג. כאן הטקסט שהוקלד הוא עשרים, עם רווחים מסביב. הטסט בודק שה-ViewModel מתעלם מהרווחים, לא מציג שגיאה, וקובע את טקסט התוצאה לשישים ושמונה מעלות פרנהייט, עם היחידה. אפשר גם לשנות את פריסת המסך בלי לשנות את כלל ההמרה. את טסט הדפדפן אנחנו עדיין שומרים, כדי לבדוק שהפקדים האמיתיים מפעילים את הפקודה ומציגים את התוצאה שלה.
טסט שני מכסה קלט שגוי. אם טקסט הקלט הוא abc, הפעלת Convert צריכה לקבוע את השגיאה ל-Enter a number, ולנקות כל תוצאה ישנה.
אז איזה טסט תופס איזו תקלה בממיר? נתחיל ב-Model. אפשר לבדוק את החישוב ישירות, למשל שאפס מעלות צלזיוס הופכות לשלושים ושתיים פרנהייט. מה שנשאר לטסט GUI הוא לוודא שהמשתמש יכול להגיע לחישוב הזה דרך הדף.
הבא בתור, ה-ViewModel. אפשר לבדוק ישירות שהקלט abc קובע שגיאה ומנקה את התוצאה. מה שנשאר לטסט GUI הוא לוודא שהשגיאה באמת מוצגת על המסך.
ל-View ולחיבורים שלו אין בדיקה ישירה. רק טסט GUI מראה שלחיצה על Convert מריצה את הפקודה ומציגה את התוצאה.
הבדיקות הישירות יכולות לכסות הרבה מקרים בלי לצייר את הממשק, וזה חשוב, כי הרצה של טסט GUI עולה יותר. צריך שהאפליקציה תרוץ, שהמסך שלה יצויר ושהפקדים יהיו מוכנים. בטסט דפדפן, זה אומר דפדפן שרץ ודף שנטען. בדיקת חישוב קטנה יכולה לקחת פחות ממילישנייה, בעוד שטסט GUI יכול לקחת כמה שניות. הזמנים האלה הם להמחשה. עלות כאן פירושה זמן ומשאבי מחשוב. הסוכן יכול לעשות את העבודה של כתיבת טסט GUI, אבל כדי להריץ אותו עדיין צריך להפעיל את האפליקציה ולצייר את המסך.
אז בדקו את מקרי החישוב הרבים ישירות, ושמרו את טסטי ה-GUI לאינטראקציות שבאמת צריכות אותם. הממיר קטן, אבל אותה הפרדה משתלמת כשהחישוב נעשה משמעותי יותר והממשק ממשיך להשתנות. כלי עזר קטנטן לא צריך שלושה חלקים. התבנית מצדיקה את עצמה כשההפרדה בין התפקידים מקלה על הבדיקה ועל השינויים.
הנה מה שאפשר לבקש מהסוכן לעשות. קודם, בקשו ממנו להציע הפרדה בין הכללים של האפליקציה, הערכים והפעולות של המסך, והפקדים שעל המסך, עם MVVM איפה שזה עוזר. בקשו ממנו להסביר את האחריויות לפני שהוא משנה קוד כלשהו. שתי שאלות יגידו לכם אם ההפרדה עובדת. האם הכללים יכולים לרוץ בלי מסך? והאם המסך יכול להציג מצב שנותנים לו? בקשו הפרדה קטנה שעונה כן על שתיהן, ולא שכתוב שרק נותן לקבצים שמות חדשים.
שנית, בקשו ממנו להוסיף ולהריץ טסטים לכללים ולהתנהגות המסך, וטסטי GUI לאינטראקציות העיקריות ולמה שהן מציגות. באפליקציית ווב, זה אומר Playwright. Playwright בודק GUI דרך הדפדפן, והוא סוג אחד של כלי לבדיקת GUI. לאפליקציות דסקטופ ומובייל יש ספריות בדיקה משלהן, והרעיון זהה. הטסט מבצע את הפעולות של המשתמש ובודק מה מופיע. בקשו ממנו לדווח על הפקודות שהריץ, על התוצאות, ועל כל טסט שלא הצליח להריץ.
לא צריך לכתוב בעצמכם כל שורה בטסטים האלה כדי לבקש את זה. ציינו את האינטראקציות ואת התוצאות שאתם מצפים להן, ואז עברו על הטסטים ועל התוצאות שלהם. אם הסוכן לא מצליח להריץ טסט GUI, בקשו ממנו להסביר מה חסר. קובץ טסט שהסוכן כתב הוא לא הוכחה שהטסט עבר.
עכשיו אתם יכולים לבקש מהסוכן לבדוק את הממשק, ולא רק את הפונקציות שמאחוריו. טסטי GUI מבצעים את הפעולות של המשתמש ובודקים את התוצאה. הפרדה של הכללים ושל מצב המסך מאפשרת לבדוק הרבה יותר מקרים ישירות, וטסטי GUI בודקים שהאפליקציה עובדת דרך הפקדים שלה. בקשו את שניהם, ובדקו מה הסוכן הריץ בפועל.