במאמר הקודם ראינו, ממש על קצה המזלג, מה זו בדיקת יחידה עם Jest בריאקט. הבעיה עם המאמר הקודם שהוא באמת ברמת ה-hello world וזה ממש ממש לא מספיק. כשאנחנו כותבים בדיקות יש לנו הרבה יותר מקומפוננטה פשוטה בלי states בכלל. הרבה פעמים יש לנו קומפוננטות מורכבות שמורכבות מקומפוננטות אחרות והתנהגות יותר מעניינת.
אחד מכלי העזר היותר חשובים שיש לנו ב-Jest זו הרכיב enzyme שהוא כרגע (נכון לגרסה 16 של ריאקט וגרסה 24 של jest) חיצוני. אבל הוא מספיק חשוב כדי שיהיה לו מקום משלו בדוקומנטציה של create react app וגם jest. בגדול הרכיב הזה עוזר לנו לבצע רינדור לקומפוננטה ולראות איך היא מתנהגת ואפילו יותר מכך – אנו יכולים ממש להתחייב למבנה של קומפוננטה ולפלט שלה.
אז בואו ונבדוק. התקנה של enzyme היא פשוטה ביותר. באפליקצית הריאקט שלכם, התקינו את המודול של enzyme באמצעות:
npm install --save-dev enzyme enzyme-adapter-react-16 react-test-renderer
מדובר במודולים שהולכים עם enzyme. עכשיו צריך ליצור קובץ הגדרות. יוצרים קובץ ב-src/setupTests.js זה קובץ ההגדרות שאפשר באמת להתפרע איתו אבל כרגע זה מה שנשים בו. תזכרו שה-configure זה אובייקט שאפשר לעשות איתו המון. מה עכשיו? כלום. רק את זה:
import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
configure({ adapter: new Adapter() });
מהנקודה הזו? אפשר לבדוק. במאמרים הקודמים דיברתי על קומפוננטה מאוד מטופשת שיש לי. קומפוננטת MyComponent שנראית כך:
import React from 'react';
class MyComponent extends React.Component {
render() {
return <p>Hello world!</p>
}
}
export default MyComponent;
ואיך הבדיקה שלה נראתה? ככה:
import React from 'react';
import ReactDOM from 'react-dom';
import MyComponent from './MyComponent';
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<MyComponent />, div);
expect(div.innerHTML).toEqual('<p>Hello world!</p>');
});
בואו ונמיר את הבדיקה הזו בבדיקה יותר מחוכמת וגם יותר עמידה. איך? עם enzyme. שימו לב:
import React from 'react';
import { shallow } from 'enzyme';
import MyComponent from './MyComponent';
it('renders without crashing', () => {
const myComponent = shallow(<MyComponent />);
expect(myComponent.contains(<p>Hello world!</p>)).toEqual(true);
});
אז מה יש לנו כאן? אנחנו מייצרים את האלמנט באמצעות shallow. מה זה אומר? אם יש לקומפוננטה עוד קומפוננטות בתוכן – הן לא ירונדרו! דבר שהוא מאוד חשוב בבדיקות יחידה כי הבדיקות האלו צריכות להיות מבודדות לחלוטין (אם אני רוצה שגם הקומפוננטות הבנות ירונדרו, אני משתמש ב-mount). שנית, אני משתמש כאן בסינטקס עם JSX וזה נחמד ופשוט. אבל יש הרבה יותר! יש לי גם את ה-API המלא של Enzyme שמאפשר לי לעשות דברים יותר מעניינים כמו למשל לבצע find ו-traversing באותו סינטקס של… jQuery. נשבע לכם ביקר לי. הנה דוגמה שבה אני בודק רק את הטקסט:
it('renders without crashing', () => {
const myComponent = shallow(<MyComponent />);
expect(myComponent.text()).toContain('Hello');
});
ואולי זו ההזדמנות לדבר גם על מבנה נכון של בדיקה. יש לנו לפני כל בדיקה describe שמכיל שלל של בדיקות שונות. וגם before שמכיל הכנה של הבדיקות. למשל, אם אני רוצה לבחון את הקומפוננטה, קובץ הבדיקות שלי יראה כך:
import React from 'react';
import { shallow } from 'enzyme';
import MyComponent from './MyComponent';
let element; // "global element to be populated in the beforeEach"
// The description of the test suite
describe('MyComponent Component is working normally', () => {
// Setup
beforeEach(() => {
element = shallow(<MyComponent />);
});
it('Checks if there is hello', () => {
expect(element.text()).toContain('Hello');
});
it('Checks if there is world', () => {
expect(element.text()).toContain('world');
});
});
יש לנו כאן בעצם describe שעוטף את הכל והוא בעצם שם כולל ל׳סט הבדיקות׳. יש לנו beforeEach שרץ לפני כל בדיקה. במקרה הזה אנחנו יוצרים את הקומפוננטה ומאכלסים אותה לתוך משתנה גלובלי שאותו אנו בודקים. זה הכל! איזה יופי 🙂
אם כבר יצא לכם לראות בדיקות אוטומטיות ו-unit test, אתם בטח תגידו ״וואו! זה ממש דומה ל-Jasmine!״ – ופה בעצם אין לי יותר מדי מה לתרום לכם חוץ מלומר לכם להשתמש ב-enzyme. אבל אם העולם של בדיקות אוטומטיות חדש לכם – תעזבו את כל מה שאתם עושים. כן, כן. תיצרו create react app – תיצרו create react app ותכניסו את MyComponent ותכתבו לו את הבדיקות לעיל. בדיקות אוטומטיות הן קריטיות לכל מוצר ומוצר.
אז ראינו את דוגמת ה-hello world. בואו ונרחיב את זה מעט. נוסיף props לקומפוננטה שלנו. איך עושים את זה בעולם האמיתי? ממש ככה. ראשית הקומפוננטה, לא משהו שיפיל אתכם עם עברתם את המאמרים הקודמים:
import React from 'react';
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.props = props;
}
render() {
return <p>Hello {this.props.greeting}!</p>
}
}
export default MyComponent;
ואיך הבדיקות הולכות בעולם האמיתי? אם אני רוצה להוסיף props, הדרך הטובה ביותר היא ליצור אובייקט של כל ה-props ואז להשתיל אותו בקומפוננטה ככה:
import React from 'react';
import { shallow } from 'enzyme';
import MyComponent from './MyComponent';
let element; // global element to be populated in the beforeEach.
let props; // "props" is global because I need to access it.
// The description of the test suite
describe('MyComponent Component is working normally', () => {
// Setup
beforeEach(() => {
// Here I put the props, in this case greeting
props = {
greeting: 'Galaxy',
};
element = shallow(<MyComponent {...props} />);
});
it('Checks if there is hello', () => {
expect(element.text()).toContain('Hello');
});
it('Checks if there is world', () => {
expect(element.text()).toContain(props.greeting);
});
});
שימו לב שהקוד הזה נראה מבלבל בהתחלה. אולי בגלל הסינטקס של השלוש נקודות […] שהוא בעצם destrdeconstructucture שלמדנו עליו במאמר על ES6. ואני משתמש בו כדי לקחת את כל האובייקט החביב של ה-props ולדחוף אותו אל הקומפוננטה. בגלל שה-props שלי הוא בסקופ העליון של הבדיקה, אני יכול להשתמש בו גם בבדיקה השניה בלי להדרש למחרוזות טקסט מיותרות.
הבדיקה הזו פשוטה, אבל המבנה שלה כבר מזכיר את מה שאנחנו עושים בחיים האמיתיים. יש עוד שימוש אחד משמעותי ל-enzyme. ואנחנו צריכים לדבר גם על מוקינג. אבל זה במאמר ההמשך.
⚠️אם אהבת את המדריכים על ריאקט – יש ספר מקיף ושלם על ריאקט שכתבתי בשם ללמוד ריאקט בעברית, במסגרת פרויקט עם חברות מובילות ומפתחים אחרים. בספר יש פירוט מקיף יותר על ריאקט ותרגילים רבים ללימוד עצמי.
2 תגובות
היי, תודה על המאמר.
שתי הערות קטנות –
– יש typo לקראת הסוף ("destrdeconstructucture" במקום "object destructuring" או משהו בסגנון)
– מאחר ש-describe() מקבל פונקציה, אפשר להכניס לתוכה את הצהרות let element ו-let props (בדוגמאות האחרונות) – כלומר לא חייבים שהם יהיו גלובליים ברמת הקובץ, אלא מספיק ברמת הפונקציה עצמה, לא?
חוץ מזה – מבוא אחלה לשימוש ב-enzyme, תודה!
הי רן – מה בעצם תורם זה שאתה מכניס את הprops או מרנדר את הקומפוננטה בתוך הbefore each? ולא פשוט איפה שהגדרת את ה let element?