היום למדתי: הפונקציה rerender של react-testing-library
הפונקציה rerender עושה בדיוק מה שהשם שלה רומז, ואני לא מאמין שהעברתי חיים שלמים בלעדיה. היא הכי שימושית כשיש לנו קומפוננה שה props שלה צריכים להשתנות במהלך הבדיקה. ככה זה עובד-
1. הקומפוננטה שאני בודק
בשביל המשחק ניקח קומפוננטה שמציגה מספר כלשהו של תיבות טקסט מסונכרנות, כשהמספר מתקבל דרך prop. אני רוצה לוודא שכשאני משנה את מספר התיבות, הטקסט בכל התיבות עדיין נשאר אותו דבר. זה קוד הקומפוננטה:
export function ManyInputs(props) {
const { n=5 } = props;
const [ value, setValue ] = useState('');
return (
<div data-testid="many-inputs">
{new Array(n).fill(0).map((_, index) => (
<input
key={index}
type="text"
value={value}
onChange={(e) => setValue(e.target.value)}
/>
))}
</div>
);
}
2. החיים בלי rerender
דרך מסורבלת לבדוק את ההתנהגות של הקומפוננטה כשמספר התיבות משתנה היא לשים את הקומפוננטה בקומפוננטת עזר שנכתוב בקוד הבדיקה. לחיצה על כפתור או שינוי טקסט בקומפוננטת העזר תשנה את מספר התיבות, וכך אפשר יהיה לראות את האפקט.
הנה בדיקה לדוגמה שמשתמשת ברעיון הזה:
test('change n', () => {
const Helper = () => {
const [n, setN] = useState(5);
return (
<div>
<label>
Number Of Boxes:
<input type="text" value={n} onChange={(e) => setN(Number(e.target.value))} />
</label>
<ManyInputs n={n} />
</div>
);
};
render(<Helper />);
let allInputs = within(screen.getByTestId('many-inputs')).getAllByRole('textbox');
userEvent.type(allInputs[0], 'hello');
for (const tb of allInputs) {
expect(tb).toHaveDisplayValue('hello');
}
userEvent.type(screen.getByLabelText('Number Of Boxes:'), '8');
allInputs = within(screen.getByTestId('many-inputs')).getAllByRole('textbox');
for (const tb of allInputs) {
expect(tb).toHaveDisplayValue('hello');
}
});
3. החיים היפים עם rerender
במקום לבזבז מקום על כתיבת קומפוננטת עזר, הפונקציה rerender יכולה לעזור לנו לשנות prop על הקומפוננטה שאותה אנחנו בודקים. הפונקציה נשלחת אלינו כשדה באוביקט שחוזר מ render, ומספיק להפעיל אותה עם ה props החדשים. הנה קוד הבדיקה המעודכן:
test('change n', () => {
const { rerender } = render(<ManyInputs n={5} />);
let allInputs = within(screen.getByTestId('many-inputs')).getAllByRole('textbox');
expect(allInputs).toHaveLength(5);
userEvent.type(allInputs[0], 'hello');
for (const tb of allInputs) {
expect(tb).toHaveDisplayValue('hello');
}
rerender(<ManyInputs n={8} />);
allInputs = within(screen.getByTestId('many-inputs')).getAllByRole('textbox');
expect(allInputs).toHaveLength(8);
for (const tb of allInputs) {
expect(tb).toHaveDisplayValue('hello');
}
});
יותר קצר, יותר מדויק והרבה יותר קל לתחזוקה.