בדיקה ירוקה יכולה לתת ביטחון. היא יכולה גם לתת ביטחון שגוי.
במוצר רב-ארגוני שעבדתי עליו היה מנגנון נוח לפיתוח: כאשר שירות ההזדהות לא היה מוגדר בסביבה המקומית, האפליקציה יצרה משתמש admin מדומה והכניסה אותו אוטומטית. זה חסך זמן למפתח, אבל יצר בעיה עמוקה יותר: מסך שנפתח בהצלחה לא הוכיח שתהליך ההזדהות באמת עובד.
אם סביבת הבדיקה עוקפת את החלק שאנחנו רוצים לבדוק בלי לומר זאת במפורש, התוצאה יכולה להיות ירוקה ומטעה.
הבעיה: נוחות שהפכה להנחה נסתרת
ההתנהגות הישנה הייתה פשוטה: שירות ההזדהות אינו זמין, ולכן יוצרים `mockUser`, מבצעים login וממשיכים למסך המערכת.
מבחינת פיתוח UI זה שימושי. אפשר לעבוד על מסכים בלי להפעיל backend או שירותי ענן. אבל מבחינת בדיקה, נוצר ערבוב בין שני דברים שונים:
- בדיקת ממשק עם נתונים מדומים.
- בדיקת תהליך login והרשאות.
כאשר הכניסה המדומה מתרחשת אוטומטית, גם מפתח וגם כלי אוטומציה יכולים להגיע ל-dashboard בלי לבצע פעולה שמבהירה שהם נמצאים ב-Sandbox. כך קל לחשוב שהבדיקה עברה מסלול הזדהות, אף שבפועל המסלול נעקף.
הבעיה אינה ש-mock הוא דבר רע. הבעיה היא bypass סמוי.
התיקון הראשון: Sandbox חייב להיות בחירה מפורשת
התיקון הראשון היה להפסיק ליצור משתמש מדומה מתוך auth listener כאשר תצורת ההזדהות חסרה.
במקום לבצע login אוטומטי, היא מסיימת את מצב הטעינה ומציגה שהכניסה לסביבת Sandbox צריכה להיעשות דרך הפעולה הייעודית במסך הכניסה.
השינוי קטן בקוד, אבל משמעותי בחוזה המוצר:
- היעדר תצורת authentication אינו שווה משתמש מחובר.
- Sandbox אינו מצב נסתר.
- בדיקת דפדפן שרוצה להשתמש בנתונים מדומים צריכה ללחוץ במפורש על כניסת Sandbox.
- בדיקה שרוצה לאמת authentication אמיתי אינה יכולה לעבור בטעות דרך mock.
זהו fail-closed מקומי: כאשר רכיב authentication אינו מוגדר, המערכת אינה ממציאה הצלחה.
התיקון השני: Integration בלי credentials ובלי Production
כדי לבדוק authentication ונתונים בצורה משמעותית יותר, נוסף מסלול Integration מבודד מול emulators מקומיים של הזדהות ומסד נתונים.
המסלול משתמש בכמה גבולות בטיחות מפורשים:
- מזהה סביבת הבדיקה מתחיל ב-`demo-`, ולכן אינו יכול להצביע לסביבה חיה.
- ה-emulators מאזינים רק על `127.0.0.1`.
- bypass של mock authentication כבוי במפורש.
- הבדיקות מקבלות טוקנים אמיתיים מ-emulator ההזדהות.
- כל fixture מקבל מזהה ריצה ייחודי.
- ה-cleanup מוחק רק את המידע שנוצר באותה ריצה.
הבדיקה יוצרת שני משתמשים סינתטיים בשני ארגונים שונים. היא יוצרת רשומה דרך ה-API, קוראת אותה בחזרה גם מה-API וגם ממסד הנתונים, מעדכנת ומוחקת אותה, ובודקת שמשתמש מהארגון האחר מקבל `403`.
היא גם מנסה לשלוח מזהה ארגון אחר מהלקוח ומוודאת שהשרת שומר את הארגון מתוך זהות המשתמש המאומת, לא מתוך payload שהלקוח יכול לשנות.
זו כבר לא בדיקת mock. זו בדיקת Integration אמיתית של החוזה המקומי, אבל עדיין לא הוכחת Production.
התיקון השלישי: לא לתת לכשל אחד להסתיר את כל התמונה
בשלב הבא ה-CI חולק למסלולים נפרדים, והתלויות שכל מסלול צריך הוגדרו במפורש.
במקום job אחד שמערבב הכול, קיימים lanes עצמאיים:
- build, type-check ו-lint.
- API smoke contracts.
- P0 browser smoke.
- Local Integration.
למסלול ה-Integration נוסף runtime חיצוני שה-emulators זקוקים לו. זו דוגמה לפרט סביבתי שאינו חלק מקוד המוצר, אבל בלעדיו הבדיקה אינה ניתנת לשחזור ב-CI.
ההפרדה נותנת אבחון טוב יותר. אם בדיקת הדפדפן נכשלת, עדיין אפשר לדעת אם ה-API smoke וה-Integration עברו. אם emulator לא עולה, הוא אינו צובע את כל שלבי האיכות ככישלון אחד חסר הקשר.
מה כל בדיקה באמת מוכיחה?
החלק החשוב ביותר הוא לנסח גבול אמת לכל lane:
- בדיקת Sandbox מוכיחה שמסע UI בסיסי עובד עם נתונים מדומים.
- API smoke מוכיח כמה חוזי HTTP בסיסיים.
- מסלול Integration מקומי מוכיח authentication, CRUD ובידוד בין שני tenants בסביבה מבודדת.
- אף אחד מהם לבדו אינו מוכיח ש-Production תקין.
כאשר השמות והמסמכים אומרים זאת במפורש, תוצאה ירוקה הופכת שימושית יותר. היא לא מבטיחה "הכול עובד". היא אומרת בדיוק איזה חוזה נבדק ובאיזו סביבה.
הלקח
תשתית בדיקות אינה רק אוסף של scripts. היא מערכת שמייצרת טענות על איכות המוצר.
אם סביבת הבדיקה נכנסת אוטומטית כ-admin, משתמשת בשירות חי בטעות או מערבבת כמה שכבות בתוך job אחד, קשה לדעת מה באמת עבר.
סביבה אמינה צריכה להיות מפורשת, מבודדת וניתנת לכשל:
- bypass רק בבחירה ברורה.
- שירותים מקומיים עם guardrails.
- credentials אמיתיים של emulator במקום mock כאשר בודקים auth.
- lanes נפרדים עם שמות שמתארים את ההוכחה.
- ניסוח ברור של מה עדיין לא נבדק.
בדיקה טובה לא רק עוברת. היא עוברת בכנות.