במאמר הקודם הסברתי על Cypress.io – כלי נהדר לבדיקות E2E. הסברתי גם על הבדיקות האלו. המאמר נגמר בדוגמה של הרצה מקומית. כלומר אני מקליד npx cypress והבדיקות שכתבתי רצות. וזה אחלה להריץ מקומית. אבל חשוב להריץ את זה בהליך CI.
רגע, מה זה CI? זה ראשי תבות של Continuous Integration. מה זה אומר? זה בעצם תהליך ארוך שהקוד החדש שאני כותב עובר ומוודא שהוא מספיק טוב כדי להצטרף לקוד הקיים של המוצר. נהתהליך הזה מורכב בדיקת קוד סטטית, בדיקות אבטחה, בדיקות ביצועים, בדיקות יחידה וכמובן E2E. את זה אנו עושים על מנת לוודא שהקוד החדש תקין. מי שרוצה לשמוע עוד על איך עובדים ב-CI\CD יכול לשמוע את הפרק שלי בפודקאסט מפתחים חסרי תרבות. בגדול – תהליך שרץ כל פעם כשאני עושה PR.
אז הבנו מה זה CI והבנו איך Cypress משתלב איתו. אם בכל פעם שאני עושה פול ריקווסט רצים גם בדיקות unit test וגם E2E אז אנחנו יכולים לדעת אם דברים נשברים. לפני שנדבר על איך משלבים את העניין הזה בתהליך ה-CI, צריך לדבר על המגבלות של E2E. ראשית, גם עם Cypress המהיר יחסית, זה תהליך ארוך אם יש הרבה בדיקות. בנוסף, אם יש הרבה בדיקות E2E חלק מהן יישברו מתישהו בלי קשר לקוד שלי. מכל מיני סיבות. בגלל זה אני נוהג לשלב ב-CI בדיקות פשוטות וספורות שנקראת Sanity. ואותן בלבד. הבדיקות האלו רצות לפני כל pull request לבדוק שהכל לא התפוצץ בגלל הקוד החדש שלי. במקביל, רץ כל ערב הסט השלם של הבדיקות שלפעמים לוקח דקות ארוכות ממש כדי לבדוק ואם הוא נשבר, צריך להבין למה ולתקן. אבל זה לא חוסם PR.
אז יצרנו כמה בדיקות למוצר שלנו, בדיקות Sanity בלבד. אני מריץ אותן מקומית עם npx cypress run. איך אני משלב אותן ב-CI שלי?
ראשית, אני צריך להחליט מול מה אני מריץ אותן. מן הסתם אני לא יכול להרית את Cypress מול פרודקשן. למה? כי אני צריך לבדוק את הקוד החדש, לא את הקוד הקיים. אני צריך להרים שרת בלוקלהוסט, לשים את הקוד החדש עליו ואז להריץ את cypress. אז ראשית – איך אני מרים שרת בלוקאלהוסט? זה תלוי בקוד שלכם. אם למשל יש לכם create react app או מקבילותיה האנגולריות/vueיות אז זה npm run start. אם זה אתר/אליקציה בפלטפורמה אחרת אחר אז זה בדרך כלל פקודה ב-bash שבונה את סביבת העבודה שלכם ומריצה אותו. יכול להיות עם דוקר או בלי. צריך לזכור ש-Cypress כתובת ב-Node.js ועובדת איתו אבל אין שום סיבה שלא להשתמש בה יחד עם קוד שכתוב בשפה אחרת. אנו נכניס את הפקודה שבונה את סביבת העבודה שלנו ל package.json באופן הזה:
וב-npm start אפשר להכניס כל קוד שהוא. למשל:
"scripts": {
"start": "python -m SimpleHTTPServer 8000",
},
או אם יש לכם דוקר:
"scripts": {
"start": "docker run myserver",
},
כמובן אם יש לכם create-react-app זה יראה כך:
"scripts": {
"start": "npm run react-scripts start",
},
מה שחשוב הוא שב-package.json תהיה תחת start (או סקריפט אחר) את הפקודה שמרימה את הסביבה שלכם.
אם אתם מקלידים npm run start הסביבה שלכם צריכה לרוץ תחת localhost. כשהיא רצה, אתם צריכים להיות מסוגלים להריץ את Cypress על הסביבה הזו. זה אומר שבמקום להכנס לכתובת של סביבת הפרודקשן, בקובץ הבדיקה צריכה להיות הכתובת של הסביבה שרצה. למשל:
context('Sanity', () => {
beforeEach(() => {
cy.visit('http://localhost:3000');
});
describe('First page is loading', () => {
it('Header is there', () => {
cy.get('body')
.find('h1')
.should('be.visible');
});
});
});
אם אני אריץ את Cypress אחרי השינוי הזה, אני אוכל לראות שהוא נכנס ל localhost:3000 ולא ל-url ומריץ את הבדיקות.
השלב הבא הוא לכתוב פקודה שתריץ את שרת הפיתוח, תריץ את הבדיקות. תסגור את שרת הבדיקות ותחזיר סטטוס כמקובל ב-BASH (כלומר 0 בהצלחה, 1 ככשלון).
את זה אנו עושים עם start-server-and-test. מודול נחמד מאוד ב-Node.js. גם הוא, כמו Cypress אגנוסטי לגמרי (כלומר אדיש) למה שהוא מריץ. מה הוא עושה? מריץ פקודה (כל פקודה), בודק אם השרת פעיל. ברגע שהוא פעיל מריץ את Cypress וברגע ש-Cypress מסיימת הורג את השרת. ככה זה נראה:
start-server-and-test start http://localhost:3000 test
הפקודות start ו-test הן הפקודות שמופיעות ב-package.json ויכולות להיות כל פקודה. למשל ככה זה באחד הפרויקטים שלי:
"scripts": {
"start": "react-scripts start",
"cypress": "cypress open",
"cypress:ci": "cypress run",
"e2e": "start-server-and-test start http://localhost:3000 cypress:ci"
},
ה-start יכול להיות כל דבר, ה-test יכול להיות כל דבר. פה אפשר לראות שאני מריץ את שרת הבדיקות הלוקלי בפורט 3000. אבל כל פרויקט עובד לפי איך שבא לו.
ברגע שאני מריץ את הפקודה npm run e2e מקומית אני רואה איך סביבת הפיתוח קמה, ה-Cypress רץ, מריץ את הבדיקות. מ סיים. שרת הפיתוח נסגר ואז הכל מחזיר את הסטטוס (מצליח או לא מצליח).
וזה? זה מה שמספיק לתהליך CI. וכשאני מריץ את הכל אני מקבל 0 (הצלחה) או 1 (כשלון). עכשיו נותר לקשור את זה ל-CI שלכם תלוי במערכת אבל מפה זה קל. במאמר הבא אני אראה איך קושרים את זה לגיטהאב actions ואיך מדבגים. שגם זה חשוב.
הערה חשובה למתכנתים שפחות מנוסים בתחום הזה: יש כאן המון מונחים שעלולים להיות חדשים או מבלבלים. המאמר הזה נוגע ללבנות flow של פיתוח ובדיקות – שזה בעצם תשתית של פיתוח. על מנת להבין בו צריך להכיר את גיט ולעבוד איתו, להכיר ולו גם באופן שטחי את Node.js להכיר לינוקס ו-CLI ולהכיר בכלל את הסייקל של הפיתוח. אל תהססו להשתמש בגוגל על כל מונח שאתם לא מכירים.
תגובה אחת
סיכום מעולה. כמה הצעות שיפור נוספות:
1. בCI להריץ את הUI מול שרת מוק. מנטרל נפילות טסטים שלא קשורות לUI
2. באופן די טרוייאלי, ההרצה של הטסטים היא אחרי בניית הקוד כדי לוודא נפילות של compilation errors.
כדאי לבחון אפשרות להשתמש בקוד המג'ונרט ולהריץ את הUI בשרת קבצים סטטיים (npm http-server) במקום הרצה עם webpack שלוקחת יותר משאבים וזמן ריצה של בנייה מחדש
3. במידה וכן משתמשים בwebpack בCI, כדאי לרוץ על mode production כי גם ככה מסמלצים שימוש באפלקציה ואין צורך ביכולות של מצב הפיתוח. יכול גם לשפר זמני ריצה.
בסופו של דבר, המוטיבציה היא להנות מהיכולות של הCI ופחות להתעייף ממנו…