• בלוג
  • עזבו אתכם נדל״ן. בדיקות יחידה הן ההשקעה החשובה באמת

עזבו אתכם נדל״ן. בדיקות יחידה הן ההשקעה החשובה באמת

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

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

1. בדיקות נכשלות? התחלה מצוינת!

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

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

The best way to be sure that your application still works after upgrading is to have good test coverage before you start the process.

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

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

  describe 'Purchasing bundles' do
    it 'should be able to buy a bundle' do
      u = create(:user)
      b = create(:bundle)
      u.purchase!(b)

      expect(u.bundles).to include(b)
    end

    it 'should check if bundle was purchased' do
      u = create(:user)
      b = create(:bundle)
      u.purchase!(b)

      expect(u.purchased?(b)).to be_truthy
    end
  end

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

# models/user.rb
def purchased?(bundle)
    !!bundle_ids.include?(bundle.id)
end

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

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

2. בדיקת יחידה טובה היא כמו קרן לייזר

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

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

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

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

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

describe('#initEvents, function() {
    it('should add event listener to button click', function() {
        const { counter, el } = createCounterAndDOMElement();
        const btn = el.querySelector('button');
        const spy = spyOn(btn, 'addEventListener');

        counter.initEvents();

        expect(spy).toHaveBeenCalledWith('click', counter.handleClick);
    });
});

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

4. אוהבים בדיקות יחידה? רוצים ללמוד על זה יותר?

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

יש באתר קורס פיתוח TDD ב JavaScript שילמד אתכם טכניקות לכתיבת בדיקות יחידה איכותיות. הקורס משתמש ב JavaScript ו Jasmine אבל האמת שהרעיונות והטכניקות שתלמדו שם יעזרו לכם בכל פרויקט שתעבדו עליו. קישור:
https://www.tocode.co.il/bundles/jasmine