אפילו שזה רק שורה

28/03/2022

משמעות הקיצור DRY היא Don't Repeat Yourself, ובהקשר של קוד אנחנו מתכוונים לא לחזור על אותו קטע קוד שוב ושוב. אבל מה אם קטע הקוד הזה הוא שורה אחת?

1. איך בודקים קומפוננטה בריאקט

ספריית הבדיקות הפופולרית לריאקט היום נקראת react-testing-library ויש בה מודול שנקרא userEvent, שמדמה פעולות של משתמש. הקוד הזה לדוגמה לוקח קומפוננטה בשם MyComponent ולוחץ על הכפתור שבתוכה עם הטקסט click me:

test('trigger some awesome feature when clicking the button', () => {
    render(<MyComponent/>)
    userEvent.click(screen.getByRole('button', name: /click me!/))
})

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

test('memory game red-blue-blue-red', () => {
    render(<MemoryGame colors=['red', 'blue', 'blue', 'red'] />);
    userEvent.click(screen.getByRole('button', name: /red/));
    userEvent.click(screen.getByRole('button', name: /blue/));
    userEvent.click(screen.getByRole('button', name: /blue/));
    userEvent.click(screen.getByRole('button', name: /red/));
    expect(screen.getByText('Bravo!')).toBeInTheDocument();
});

2. ואיך זה נשבר?

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

ראיתם מה קרה עכשיו לבדיקות?

בשביל שהבדיקה תחזור לעבוד צריך להחליף כל מקום שמופיעה השורה:

userEvent.click(screen.getByRole('button', name: /red/));

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

3. מה עושים במקום?

הלקח כאן הוא ש DRY לא קשור בכלל למספר השורות שחוזרות על עצמן אלא למשמעות של הפעולה - קוד הבדיקה הראשון שכתבתי חיבר בין "לחץ על הכפתור האדום" ל"לחץ על הכפתור האדום בדרך של userEvent ועם מבנה ה DOM של הקומפוננטה MemoryGame". החלק שחזר על עצמו שוב ושוב בקוד היה "כדי ללחוץ על הכפתור האדום צריך למצוא אלמנט כפתור עם הטקסט red". המשפט הזה הופיע בצורה מובלעת כל פעם שכתבתי את שורת הלחיצה.

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

function clickOnRed(screen) {
    userEvent.click(screen.getByRole('button', name: /red/));
}

את קוד הבדיקה אני אחליף עכשיו ל:

test('memory game red-blue-blue-red', () => {
    render(<MemoryGame colors=['red', 'blue', 'blue', 'red'] />);
    clickOnRed();
    clickOnBlue();
    clickOnBlue();
    clickOnRed();

    expect(successTextElement()).toBeInTheDocument();
});

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